Testing custom redux middleware

11.3k views Asked by At

How do I unit test custom redux middleware? I have this simple function that is supposed to dispatch action after a given timeout but ... I have no idea how to approach it. I dint find enough resources regarding this issue.

const callAPiTimeoutMiddleware = store => next => (action) => {
  if (action[CALL_API]) {
    if (action[CALL_API].onTimeout) {
      setTimeout(() => {
        store.dispatch({ type: action[CALL_API].onTimeout });
      }, DEFAULT_TIMEOUT_LENGTH);
    }
  }

  return next(action);
}

These are my tests based on blog post that is mentioned in the accepted answer:

describe('callAPiTimeoutMiddleware', () => {
  describe('With [CALL_API] symbol', () => {
    it('should dispatch custom action', () => {
      const clock = sinon.useFakeTimers();
      const fakeStore = { dispatch: sinon.spy() };
      const fakeNext = sinon.spy();
      const fakeAction = {
        [CALL_API]: {
          endpoint: 'endPoint',
          method: 'METHOD',
          types: ['REQUEST_TYPE', 'SUCCESS_TYPE', 'FAILURE_TYPE'],
          onTimeout: 'TIMEOUT_TYPE',
        },
      };

      callAPiTimeoutMiddleware(fakeStore)(fakeNext)(fakeAction);
      clock.tick(99000);

      expect(fakeStore.dispatch.calledOnce).toEqual(true);
    });


    it('should call next action', () => {
      const fakeStore = { dispatch: sinon.spy() };
      const fakeNext = sinon.spy();
      const fakeAction = {
        [CALL_API]: {
          endpoint: 'endPoint',
          method: 'METHOD',
          types: ['REQUEST_TYPE', 'SUCCESS_TYPE', 'FAILURE_TYPE'],
          onTimeout: 'TIMEOUT_TYPE',
        },
      };

      callAPiTimeoutMiddleware(fakeStore)(fakeNext)(fakeAction);

      expect(fakeNext.calledOnce).toEqual(true);
    });
  });

  describe('Without [CALL_API] symbol', () => {
    it('should NOT dispatch anything', () => {
      const clock = sinon.useFakeTimers();
      const fakeStore = { dispatch: sinon.spy() };
      const fakeNext = sinon.spy();
      const fakeAction = { type: 'SOME_TYPE' };

      callAPiTimeoutMiddleware(fakeStore)(fakeNext)(fakeAction);
      clock.tick(99000);

      expect(fakeStore.dispatch.calledOnce).toEqual(false);
    });


    it('should call next action', () => {
      const fakeStore = { dispatch: sinon.spy() };
      const fakeNext = sinon.spy();
      const fakeAction = { type: 'SOME_TYPE' };

      callAPiTimeoutMiddleware(fakeStore)(fakeNext)(fakeAction);

      expect(fakeNext.calledOnce).toEqual(true);
    });
  });
});
2

There are 2 answers

0
Thaadikkaaran On BEST ANSWER

The redux-thunk middleware has unit tests written. You can refer that here

He is using only mocha and chai.

And here he is talking about how you can write unit tests for your redux app.

Nice blog on redux middlewares and how to write unit tests for them.

2
Gegenwind On

I needed some more details to actually create tests for the middleware, so here is my answer specific to the middleware test:

Middlewares can be tested very easily without doing a lot of mocking. The chained arrow functions appear intimidating, but it is just a matter of how they are called. I used jest to help me testing but you can replace all jest functions with something else. I'll get right into the code and explain in comments:

import logger from './logger'; //this is your middleware
const next = jest.fn(); // middleware needs those as parameters, usually calling next(action) at the end to proceed
const store = jest.fn(); 

const action = { type: 'YOUR_ACTION_TYPE', payload: { your: 'data' }}
logger(store)(next)(action);

This is how you call your middleware in a test. In your case if you want to check if dispatch has been called you could do something like this:

// Add this in the beginning
store.dispatch = jest.fn();

// Add this after calling logger()
expect(store.dispatch.mock.calls).toEqual('whatever calls you expected');