1

I am trying to write unit tests for a redux application. I have a common store.js:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

import rootReducer from './root-reducer';

export default createStore(rootReducer, applyMiddleware(thunk));

And the test file:

// necessary imports and mocks

describe('store', () => {
  beforeEach(() => {
    redux.createStore.mockClear();
    redux.applyMiddleware.mockClear();
    jest.resetModules()
  });

  it('should create the store properly', async () => {
    const stubMiddleware = jest.fn();
    redux.applyMiddleware.mockImplementation(() => stubMiddleware);

    await import('./store');

    expect(redux.createStore).toHaveBeenCalledTimes(1);
    expect(redux.createStore).toHaveBeenCalledWith(rootReducer, stubMiddleware);
  });

  it('should apply thunk middleware', async () => {
    redux.applyMiddleware.mockImplementation(() => {});

    await import('./store');

    expect(redux.applyMiddleware).toHaveBeenCalledTimes(1);
    expect(redux.applyMiddleware).toHaveBeenCalledWith(thunk);
  });
});

Unfortunately, the second test case will fail, since the store module is evaluated only once at the first import and I clear all the mocks after each test.

Actually the problem is pretty specific, but the question is broader. We have many tools where we need to add side-effects to our modules, like here, creating a store. How can these side-effects get tested? My first solution was exporting a getStore() method which was kind of an implementation of the singleton DP, but again, in that case, the module was stateful by the singleton instance which might require re-evaluation in certain cases.

I've also tried jest.resetModules() and jest.isolateModules() but they didn't help.

ikarasz
  • 253
  • 2
  • 10
  • https://stackoverflow.com/questions/48989643/how-to-reset-module-imported-between-tests is similar but `require` didn't do the trick neither – ikarasz May 14 '20 at 16:30
  • In general: just put it inside a function. – Bergi May 14 '20 at 20:09
  • For specific cases, you sometimes have control over the module loader and can use tricks to reload the module, like in Jest tests. But using a function is simpler and works everywhere. – Bergi May 14 '20 at 20:10
  • @Bergi I don't really understand, it is already in a function, the second parameter of the test-case is a function – ikarasz May 15 '20 at 08:14
  • I mean you should put the code that you want to re-evaluate in a function, in this case the `createStore(rootReducer, applyMiddleware(thunk))` . – Bergi May 15 '20 at 13:12
  • I've mentioned my problem with that approach, it would create more redux store in the application – ikarasz May 15 '20 at 14:43
  • Are you referring to the `getStore()` function you mentioned? I was not suggesting a stateful singleton pattern which creates only one store, I meant a factory function that creates a new store every time you call it. – Bergi May 15 '20 at 18:52
  • Yes, it would be an approach especially if I would use react and react-redux then it is easy to pass the store to the provider, but I use redux separately and I import it in multiple files, so it would create more redux stores in my application. – ikarasz May 16 '20 at 08:30
  • You still can create a single instance in your *main.js* or *app.js* (or even in *store.js*) and then import that everywhere if you need it, but when you need multiple instances for testing then you should not use the singleton pattern. – Bergi May 16 '20 at 12:58
  • We can think of `export default createStore(...)` as singleton, since the module gets evaluated only once and will always export the same store. I Like this approach and I don't want to add more layers to the instantiation. Also, if I create a factory method, I will need to unit test them, where I will face the same issue. I will have a module scoped variable which won't be testeable without re-evaluating another module. Of course only if I want to keep the one testCase one assertion method. – ikarasz May 16 '20 at 13:32

0 Answers0