Andrew
Andrew

Reputation: 537

Mocha testing stubbed ajax call with a .then

I've got a jquery ajax call that looks something like this:

$.ajax({
  type: "GET",
  dataType: 'json',
  data: data,
  url: url
}).then((data, successFlag, xhr) => {
  this.props.someFunc(data)
})

In my test file, I have the jquery ajax call stubbed out with sinon and it returns a resolved promise with data:

sinon.stub($, 'ajax')
  .returns(Promise.resolve({ data: 'test data' }))

And I'm also spying on my someFunc(data) function. In my test, I'm calling a function that makes the ajax call, and then expecting my someFunc(data) to be called. However, the expectation fails, but when I put a console log in my someFunc(data) function, I can see that it is clearly being called:

component.instance().makeAjaxCall()
expect($.ajax.calledOnce).to.be.true // passes
expect(someFuncSpy.calledOnce).to.be.true // fails

Now I assume that it's failing because it's checking the expectation before then .then executes and I tried looking up some solutions dealing with testing with promises but nothing I've tried works so far (or I'm implementing it wrong). How can make sure that the .then finishes executing before I check the expectation?

Upvotes: 2

Views: 1354

Answers (3)

estin sunny koothoor
estin sunny koothoor

Reputation: 201

Use done() after call the stub method.

it('should make an ajax call', function(done) {
    sinon.stub($, 'ajax').returns(Promise.resolve({ data: 'test data' }))
    component.instance().makeAjaxCall()
    expect($.ajax.calledOnce).to.be.true;
    done(); // let Mocha know we're done async testing

    expect(someFuncSpy.calledOnce).to.be.true;
});

Note: Please pass done as argument in function -- it('', function(done) {})

Source

Upvotes: 1

Fiddles
Fiddles

Reputation: 2916

Enter sinon's excellent mocking utils sinon.createFakeServer(); (http://sinonjs.org/releases/v4.1.2/fake-xhr-and-server/)

Setup the mocks and fake server for the test, call the function, tell fake server to respond, then check expectations.

In this case, something like:

it('should call someFunc with the expected data', function () {
    var server = sinon.createFakeServer();
    server.respondWith("GET", "*",
            [200, { "Content-Type": "application/json" },
             '[{ "id": 12, "comment": "Hey there" }]']);
    var comp = component.instance();
    var testStub = sinon.stub(comp.props, 'someFunc');
    comp.makeAjaxCall();
    this.server.respond();

    expect(testStub.calledOnce).to.be.true; // You should consider the sinon-chai package for nicer assertion debugging
    testStub.restore();
    server.restore();
}

Personally, I like being able to pass in mocked dependencies instead, as I find it more testable (eg. pass in $.ajax to the constructor, stored as an ajaxService parameter on the instance).

Upvotes: 0

felixmosh
felixmosh

Reputation: 35583

You should "register" on the ajax Promise, and put your expectation in the then block.

Something like that,

component.instance().makeAjaxCall().then(() => {
    expect($.ajax.calledOnce).to.be.true;
    expect(someFuncSpy.calledOnce).to.be.true;
});

It not worked for you because Promise register a callback on the micro task queue, and it got run on the next tick.

Upvotes: 0

Related Questions