简体   繁体   中英

Mocha - Testing Promise, `done` never gets called in Promise

I'm trying to test a spy that would be called in the .then block of a promise, but the done in the then block doesn't seem to be executed at all.

I'm getting timeout of 2000ms exceeded.

Here's what I'm testing (async):

/**
 * Passed down to the LoginForm component to
 * handle form submission.
 */
_submitHandler(data) {
  return function(evt) {
    evt.preventDefault && evt.preventDefault();
    evt.stopPropagation && evt.stopPropagation();

    return request('post', 'auth', data)
      .then((res) => {
        AuthActions.login();
        return res;
      })
  }
}

Here's my test:

describe('when it succeeds', () => {
  it('should login', (done) => {
    sinon.spy(AuthActions, 'login');

    Instance._submitHandler({})({})
      .then((res) => {
        console.log('Called!!!');
        expect(AuthActions.login.called).to.equal(true);
        AuthActions.login.restore();
        done();
      }, done);
  });
});

I'm using Karma to run my tests; Chai and Sinon.

I also had this issue, the cause was trying to respond to the XHR before the connection was opened, eg

This code will throw INVALID_STATE_ERR - 0 from FakeXMLHttpRequest.setResponseHeaders :

describe("get", function () {

  beforeEach(function () {
    this.xhr = sinon.useFakeXMLHttpRequest();
    this.xhr.onCreate = function (request) {
      request.respond(200, null, "");
    };
  });

  afterEach(function () {
    this.xhr.restore();
  });

  it("should make a request", function () {
    myObject.get("testurl");
  });

});

This code works:

describe("get", function () {

  beforeEach(function () {
    this.xhr = sinon.useFakeXMLHttpRequest();
    this.requests = [];
    this.xhr.onCreate = function (request) {
      this.requests.push(request);
    };
  });

  afterEach(function () {
    this.xhr.restore();
  });

  it("should make a request", function () {
    myObject.get("testurl");
    this.requests[0].respond(200, null, "");
  });

});

Reading the documentation, which itself does show the same technique of pushing to a requests array, I had subconsciously and inaccurately came away with the impression that onCreate, despite its name, was more like "on request".

myObject.get = function (url) {
  var http = new XMLHttpRequest(); 
  // the XHR instance exists
  // readyState is 0
  // onCreate runs now
  http.open("GET", url); 
  http.send(); 
  // readyState is 1
  // can call setResponseHeaders and respond 
}

The result is that you have to put your respond code after you call the method that runs XMLHttpRequest 's send , as:

myObject.get("testurl");
this.requests[0].respond(200, null, "");

I was using the respond method, but the same is true for setResponseHeaders ( respond calls setResponseHeaders )--in your test it was called too early.

I've finally solved this issue after hours. It looks like the then block wasn't being called because an exception was being thrown due to xhr .

Let me elaborate. I was using sinon 's FakeXMLHttpRequest , like so:

var requests, xhr;
beforeEach(() => {
  requests = [];
  xhr = sinon.useFakeXMLHttpRequest();
  xhr.onCreate = (req) => {
    req.setResponseHeaders({ /* */ });
    requests.push(req);
  }
});

By putting a console.log on the catch block, I found out that I was getting the error was INVALID_STATE_ERR EXCEPTION 0 . This leads me to the conclusion that xhr was the problem all along.

And then I found out about sinon 's fakeServer , and used it instead (but I don't think this is actually the solution to this problem). Not really related, but I also used sandbox here because saved me from writing countless of .restore for stubs, etc.

describe('when it succeeds', () => {
  var sandbox, server, Instance;
  beforeEach(() => {
    sandbox = sinon.sandbox.create();
    sandbox.useFakeServer();
    server = sandbox.server;
    server.respondWith([200, { "Content-Type": "application/json" }, JSON.stringify({ data: { token: '' }})]);
    sandbox.spy(AuthActions, 'login');
    Instance = TestUtils.renderIntoDocument(<Component />);
  });
  afterEach(() => {
    sandbox.restore();
  });
  it('should login', (done) => {
    Instance._submitHandler({})({})
      .then(() => {
        expect(AuthActions.login.called).to.equal(true);
        done();
      }, done);

    setTimeout(() => {
      server.respond();
    }, 0);
  });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM