I have an asynchronous action creator which is split up into two actions, one to signal the start of an action and another to signal the end. The web request in the middle of the asynchronous action creator relies on the values set by the first synchronous dispatch:
export function changeFilter(from, to) {
return (dispatch, getState, { fetch }) => {
// Updates the state with the new values and sets a loading flag
dispatch(changeFilterRequest(from, to));
// Now I want to use those newly-set values to build a query
// string. However, the stubbed implementation of `dispatch`
// doesn't use reducers, so `getState` continues to return the
// original value.
const { activeUsers } = getState();
return doSomeWebRequest(fetch, activeUsersParams(activeUsers))
.then(response => dispatch(changeFilterSuccess(response)))
};
}
The implementation of redux-mock-store's dispatch is pretty sparse, and in (obvious) hindsight, we never pass any reducers to the mocked store.
This means that the first dispatch
call has no chance of updating the state that the second dispatch call relies on. The web request that we make then has the original values for the to
and from
dates, not the updated ones from the async action call.
I'd like to avoid directly using the from
and to
values passed in to changeFilter
as there may be further transformations applied to the parameters within changeFilterRequest
. Relying directly on the result of getState
will also allow me to DRY up the handful of similar methods that filter in different ways.
Are there any patterns I should be following for these types of tests? Is there a different way that I should be structuring my action creators?
I think your unit test should be precise and focus only on the scope of the function you want to test which is changeFilter
. Assuming you have another unit test for changeFilterRequest
that tests how activeUsers
is calculated and added to the store , the fact that activeUsers
is created in the store as a side effect of dispaching changeFilterRequest
should be out of scope of your test as changeFilter
shouldn't care how activeUsers
is calculated. I think you can safely add any dummy value for activeUsers
in your mocked store that is not necessarily dependent on the values of to
and from
that you pass to changeFilterRequest
and make sure your function is reading that value properly from the store and passing it to activeUsersParams
.
That said, I highly recommended redux-saga
as an alternative to redux-thunk
to handle async operations just because it makes testing these kind of actions easy without having to create mocks or test side effects since all your actions will be pure.