Testing Async Redux Actions with Jest
I recently ran into a problem with testing out asynchronous actions in react-redux. I didn’t quite know what I needed to do to expect the proper result of a successful async promise inside of an action. As a result, I had to dig through quite a few articles to piece it together. It’s my hope that with this article I save you some time and hopefully get you going in testing out your async actions in react with react-redux.
Our app code
There’s quite a few pieces that made this problem a bit complicated, and that included:
- The use of a third-party script (Firebase)
- Using react-redux with redux-thunk (Setting up a mock store)
Here is an action that is used when a person clicks the “Sign In” button in my app:
We want to examine the signIn function. This function returns another function that executes auth.signInWithEmailAndPassword
. We’ll come back and examine the authentication module. But this code essentially dispatches the onSignIn
method on success and dispatches onSignInError
on failure.
Preliminary test setup
To properly test this function, we want to test that dispatch
gets called with onSignIn
and is passed the user
object returned by the response. To be honest, I wasn’t quite sure how to do that yet, so I researched several articles and pieced it together:
Let’s take a close look at our signIn test above.
- First, I’m importing the action type I’m going to test against.
- Next, I import
configureStore
,thunk
andinitialState
so that I can setup a mock store todispatch
thesignIn
action. - Then, I define my middlewares and pass that into the
configureStore
call to create amockStore
method. - Then, I create my mocked store with the initial state of the app.
- Finally, I
dispatch
thesignIn
action with anemail
andpassword
to ultimately test against the actions that were called.
The idea here is that store.getActions()
will give us the details on what actions were dispatched after the signIn call. We’re then evaluating the response of the signIn action which should be [ { type: ON_SIGN_IN, user } ]
.
But, this test fails. Welp.
It fails because we haven’t taken into account two items:
- The third-party script (Firebase)
- That we’re working with promises
So let’s first figure out how to deal with third party scripts.
Mocking out third party implementations
Remember that authentication object that we were importing:
This is what the firebase module looks like in this app:
The above firebase module configures and initializes firebase with the values of some environment variables. The important part here is that we initialize a database
and auth
object and export out those instances from this module.
We don’t really care about firebase in our tests. In fact, we don’t want to interface with firebase. So we need to mock that sucker in our tests. But, how do we do that? With Jest! Jest comes with mocking out of the box. Let’s see how this is done.
Here is the signIn
test again, but it’s been updated with the following:
- Imports
auth
from our firebase service - Sets
auth.signInWithEmailAndPassword
with a mocked function - Which returns a
Promise
that resolves with a user object
So now we have our firebase implementation mocked properly. But this test still fails! Why?
Using Async/Await
Let’s go back and see what auth.signInWithEmailAndPassword
does in our app code:
It’s a promise chain. But our test is currently setup for synchronous procedures. So we need to configure our test to execute in an async fashion. We do this by utilizing async/await, defining our test as an async function and awaiting on the dispatch, like so:
As you can see we now define the it block function as an async
function:
it('dispatches the onSignIn action on success', async () => {
And we await the store.dispatch
:
await store.dispatch(signIn('the.batman@dc.com', 'test1234'));
This ensures that the expect is run after the promise is resolved by our mock.
We can now run our tests and see that this passes. You successfully know how to test your async react-redux actions with ease. If you have any problems or questions feel free to ping me at @alvincrespo on Twitter. Cheers!
Bonus!
Need to mock many methods from an npm package?
Jest has a handy function called genMockFromModule.
Need to mock that library in every test?
Simply create a file in the __mocks__
folder with the same name:
References
Tutorial For Adding Redux to a React App
Stack Overflow: How can I mock an ES6 module import using Jest?
Stack Overflow: jest redux-thunk test if action of same module is dispatched
Jest Documentation: jest.spyOn(object, methodName)
Jest Documentation: Mock Implementations