1
votes

I've got a redux action creator that utilizes redux-thunk to do some logic to determine what to dispatch to the store. Its not promise-based, like an HTTP request would be, so I am having some issues with how to test it properly. Ill need a test for when the value meets the condition and for when it doesn't. Since the action creator does not return a promise, I cannot run a .then() in my test. What is the best way to test something like this?

Likewise, I believe it would be pretty straightforward testing the getRemoveFileMetrics() action creator as it actually does return a promise. But how can I assert that that will called if the value is removeFiles and meets the condition? How can that be written in the test?

Thanks in advance as this has had me stuck for the last couple of days.

Action Creators

export const handleSelection = (value, cacheKey) => {
    return dispatch => {
        if (value === "removeFiles") {
            dispatch(getRemoveFileMetrics(cacheKey));
        }
        dispatch({ type: HANDLE_SELECTION, value });
    };
};

export const getRemoveFileMetrics = cacheKey => {
    return dispatch => {
        dispatch({ type: IS_FETCHING_DELETE_METRICS });
        return axios
            .get(`../GetRemoveFileMetrics`, { params: { cacheKey } })
            .then(response => {
                dispatch({ type: GET_REMOVE_FILE_METRICS, payload: response.data });
            })
            .catch(err => console.log(err));
    };
};

Jest

it("should dispatch HANDLE_SELECTION when selecting operation", () => {
    const store = mockStore({});
    const value = "switchVersion";
    const expectedAction = [{
        type: MOA.HANDLE_SELECTION,
        value,
    }]; // TypeError: Cannot read property 'then' of undefined
    return store.dispatch(MOA.handleSelection(value)).then(() => {
        const returnedActions = store.getActions();
        expect(returnedActions).toEqual(expectedAction);
    });
});

NEW EDIT

So based off of Danny Delott's answer to return a promise, I acheived a passing test as follows:

export const handleSelection = (value, cacheKey) => {
    return dispatch => {
        if (value === "removeFiles") {
            return dispatch(getRemoveFileMetrics(cacheKey));
        }
        return new Promise((resolve, reject) => {
            resolve(dispatch({ type: HANDLE_SELECTION, value }));   
        });
    };
};
1

1 Answers

1
votes

Is there a reason to explicitly NOT return a promise in your action creator? It looks like getRemoveFileMetrics is returning the promise, it just gets swallowed in handleSelection...

Easiest solution is to just return the promise:

export const handleSelection = (value, cacheKey) => {
    return dispatch => {
        if (value === "removeFiles") {
            return dispatch(getRemoveFileMetrics(cacheKey));
        }
        dispatch({ type: HANDLE_SELECTION, value });
        return new Promise();
    };
};

Otherwise, you'll need make your assertions after the event loop is finished. You can do with a setTimeout wrapped in a Promise to get the .then behavior.

it("should dispatch HANDLE_SELECTION when selecting operation", () => {
    const store = mockStore({});
    const value = "switchVersion";
    const expectedAction = [{
        type: MOA.HANDLE_SELECTION,
        value,
    }];

   store.dispatch(MOA.handleSelection(value));

   // flush outstanding async tasks
   return new Promise(resolve => {
     setTimeout(resolve, 0);
   })
   .then(() => {
      const returnedActions = store.getActions();
      expect(returnedActions).toEqual(expectedAction);
    });
});