Umair Sarfraz
Umair Sarfraz

Reputation: 5953

Mocha: Ensure the done() callback is being called in this test

I have written a small interceptor using axios that would flush out localBrowserStorage and would redirect user to login page if response code is 401. It works fine but I am getting some errors in the unit test.

Test

describe('Api Service', () => {
  let sandbox;

  beforeEach(() => {
    moxios.install();
    sandbox = sinon.sandbox.create();
  });

  afterEach(() => {
    moxios.uninstall();
    sandbox.restore();
  });

  describe.only('interceptors', () => {
    it('clear storage and redirect to login if response status code is 401', (done) => {
      moxios.withMock(() => {
        sandbox.spy(browserHistory, 'push');
        sandbox.spy(storage, 'clear');

        axios.get('/test');

        moxios.wait(() => {
          const request = moxios.requests.mostRecent();
          request.respondWith({
            status: 401,
            response: {}
          }).then(() => {
            expect(browserHistory.push).to.have.been.calledWith('/login');
            expect(storage.clear).to.have.been.called; // eslint-disable-line no-unused-expressions
            done();
          });
        });
      });
    });
  });
});

I get these two warnings with this:

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Request failed with status code 401
(node:5338) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): AssertionError: expected push to have been called with arguments /login

And this error:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

EDIT:

axios.interceptors.response.use((response) => {
  if (response.status === 401) {
    storage.clear();
    browserHistory.push('/login');
    return response;
  }
  return response;
});

Upvotes: 1

Views: 1464

Answers (1)

robertklep
robertklep

Reputation: 203304

You need to pass rejections back to Mocha by calling done in the catch handler:

request.respondWith({
  status: 401,
  response: {}
}).then(() => {
  expect(browserHistory.push).to.have.been.calledWith('/login');
  expect(storage.clear).to.have.been.called; // eslint-disable-line no-unused-expressions
  done();
}).catch(done);

If you don't handle rejected promises, there is a chance that done never gets called (resulting in a timeout).

For instance, if one of the expectations fails, it will throw an error. When that happens, the call to done following the expectation lines will never get executed. You can (and should) catch the error with a .catch clause, as above. That should call done with the error. The code I'm using is short for:

.catch(function(err) {
  done(err);
})

Upvotes: 4

Related Questions