samedi 27 février 2016

Testing non-promises after promise completes with Mocha, Chai and Sinon

I've created a small learning project in Redux, and have essentially copied the middleware for asynchronous actions from the documentation. Now I want to write some tests for it, to make sure that it will work fine after I change around some things.

To write my tests, I'm using Mocha, with chai, chai-as-promised and sinon.

I have no problems with redux in any way, but I'm unsure of how to test things.

The relevant code from the middleware:

export default function callApiMiddleware({ dispatch }) {
  return next => action => {
    // ... various checks

    const [ requestType, successType, errorType ] = types;
    dispatch({ ...payload, type: requestType });

    return callApi().then(
      response => dispatch({ ...payload, type: successType, response }),
      error => dispatch({ ...payload, type: errorType, error })
    );
  }
}

The middleware dispatches the appropriate action whether the promise is fulfilled or rejected.

To test this, I'm stubbing out the dispatch function, along with others, and I also pass in a dummy promise, that I fulfill or reject depending on what I want to test.

A test I have written looks like this:

it('should trigger success action when successful', () => {
  let promise = callApiMiddleware({ dispatch, getState })(next)({
    ...action,
    callApi: () => new Promise((resolve, reject) => resolve('SUCCESS'))
  });
  return expect(promise).to.eventually.be.fulfilled;
});

This works fine, but the first problem I ran into was when I tried to simulate a rejected promise that could result from no internet connection, so I wrote this test:

it('should trigger failure action when failure occurs', () => {
  let promise = callApiMiddleware({ dispatch, getState })(next)({
    ...action,
    callApi: () => new Promise((resolve, reject) => reject('ERROR'))
  });
  return expect(promise).to.eventually.be.rejected;
});

But this fails with the following message:

AssertionError: expected promise to be rejected but it was fulfilled with undefined

Expected :[undefined]

Actual :[undefined]

This makes no sense to me, since I clearly pass in a promise that's only function is to be rejected.

My other problem is that I also want to make other assertions, that don't necessarly have anything to do with the promise itself, but have to be evaluated when the promise finishes e.g. I want to assert that the dispatch method was called twice. The dispatch method itself is stubbed out in the test using sinon in order to perform the desired assertions. Potentially, I want to make multiple assertions in this way.

I have tried the following:

it('should trigger success action when successful', () => {
  let promise = callApiMiddleware({ dispatch, getState })(next)({
    ...action,
    callApi: () => new Promise((resolve, reject) => resolve('SUCCESS'))
  });
  return Q.all([
    expect(promise).to.eventually.be.fulfilled,
    expect(dispatch).to.eventually.be.calledTwice
  ]);
});

But this returns some very large error that simply tells me that dispatch is not thenable i.e. not a promise.

I am out of ideas on how to do this, so any input would greatly be appreciated.

Aucun commentaire:

Enregistrer un commentaire