Search code examples
react-reduxjestjsredux-thunk

How can I check in a jest test if a thunk action within a thunk action creator has been dispatched?


Here's a generalized example:

// myActions.js
export const actionOne = () => (dispatch) => {
    dispatch(actionTwo());
};

export const actionTwo = () => ({
    type: 'SOME_TYPE',
});

I would like to test that actionTwo has been either called or dispatched, ideally without the test knowing anything about what is going on in actionTwo, because I have a different test that takes care of that.

I am using redux-mock-store to dispatch the tested action to a mocked store and calling store.getActions() to find out if the expected actions within the thunk action creator have been dispatched. I feel it is not the right way to go in this particular scenario because then the test would test more than it should. I really only want to know if actionTwo has been called at all.

I'm aware of spyOn and jest.mock, but I've been unable to use either to solve my problem. Here's what the generalized test looks like:

// myActions.test.js
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from 'myActions';

const mockStore = configureMockStore([thunk]);

test('actionOne', () => {
    const store = mockStore();

    return store.dispatch(actions.actionOne()).then(() => {
        // TODO: check if actions.actionTwo was called
    });
});

test('actionTwo', () => {
    const store = mockStore();

    return store.dispatch(actions.actionTwo()).then(() => {
        expect(store.getActions()).toEqual([{ type: 'SOME_TYPE' }]);
    }); 
});

I'm grateful for any suggestions!


Solution

  • Took me a while, but I figured it out. It's not ideal (because it involves a small change to the tested code), but the closest to ideal that I could get.

    // myActions.js
    export const actionOne = () => (dispatch) => {
        dispatch(exports.actionTwo());
    };
    
    export const actionTwo = () => ({
        type: 'SOME_TYPE',
    });
    

    The important change is the exports.actionTwo(). That way, I make sure that I can overwrite the function's implementation from the outside (the test file) and the overwriting function will actually be called from within the imported file.

    Now I can simply add something like the following to my test file:

    beforeEach(() => {
        actions.actionTwo = jest.fn(() => () => Promise.resolve());
    });
    

    actionTwo is now being mocked and I can use toBeCalledWith and other expectations on it. If I wish to test its actual implementation within the same test file, I can store it in a variable before calling beforeEach, like:

    const actionTwo = actions.actionTwo;
    

    And then in the test setup for its implementation, I can overwrite the mock calling

    actions.actionTwo = actionTwo;
    

    That's it. Now I can make sure to ignore all side effects from an exported function and test it as an actual unit.