⚠️ This lesson is retired and might contain outdated information.

React Testing: Redux Reducers

Trevor Miller
InstructorTrevor Miller

Share this video with your friends

Send Tweet
Published 8 years ago
Updated 2 years ago

Sometimes we want to test our Redux reducers to make sure they work as expected. In this lesson we will walk through setting up some Redux reducer tests for common situations and edge cases.

[00:01] First I'll import my assertion library, then I'll import my reducer that I'm going to be testing. So in this case it's my theme, next let's create our describe block and we'll call that themeReducer to match our file that we're testing, and then we'll give that a function. Inside of that function let's create our it block, and we'll say that it should change the theme color.

[00:26] Next I'm going to write my assertion, so I'm going to say that we're expecting our actual value to equal our expected value, and now I'm going to write my expected variable so we'll say expected, and we're going to give this an object, because that's what the Redux slice that we're dealing with is going to give us. So we're dealing with the theme slice of our store. So inside of that, we're going to expect that when we fire an action to change the theme color that we'll get back the color.

[01:01] So I'm going to put in a random color here, and now for our actual value, we're going to need to call our reducer, so I'll say themeReducer and then the first argument is going to be the previous state, so let's create a function that will give us our previous state. I'm going to call it stateBefore. Then we're also going to need to pass this reducer an action, and we'll be creating that in just a sec.

[01:27] So now let's create our function and I'm going to say function and we'll call it stateBefore to match what we passed into our reducer, and we just want this to be a little factory function that returns what our state should look like before we run our reducer. So I'm going to just return an object and inside of that object it's going to have a color property just like in our expected object, but we want to use a different value this time to make sure that our action is changing it properly.

[01:55] I'm going to put in a random color here, so now let's go down and create our action object. Inside of that object we're going to have a type which is going to be updateThemeColor. Now let's pass a payload which is going to contain the new color that we want to update our state tree to match. So we're going to say color, and we're going to use this same color here that we've put in our expected state tree after this action is run. So now let's clean this up a little bit.

[02:26] I'm going to go to this empty line and delete this, and then add an extra line up here. Let's review what we've done here. We've called our reducer with our previous state and our action which our previous state is just the factory returns, and then our action tells us which action to use as well as the new color that should be updated in the state tree, and so our state, our final slice of the state dealing with the theme, we're expecting it to just be this new color.

[02:57] So the reason why I added this factory function here is so that we can get a pure previous state every time in case we want to add more tests. If we were adding more tests here it would make sense to pull this function out above this test, and then to use this same state before in other tests so that we get a clean state in each of our reducer tests. Let's show an example of that now by making some tests for our quote reducer.

[03:23] Here I've created my quote reducer test file, and I've imported my assertion library as well as my quote reducer, just like we did in the last test, and then what I've done is created another stateBefore function which returns what we want our initial state to look like for our test. So for these quotes, I've added two quotes here with all the different properties that expected in each of those quotes. So now let's go down below this function, and let's create some tests here.

[03:55] I'm going to say that it should add quotes by id, so now just like in our last test, we're going to need an action object, we're also going to need an actual value which is going to be the result of calling are quote reducer with our stateBefore function and our action. We'll also need our expected value which is going to be an array of quotes, and then finally let's write our assertion that we expect our actual value to equal our expected value. So now let's go fill in each of these pieces.

[04:24] For our action we're going to need a type of AddQuoteById, and then our payload is going to be a quote object, so I've pasted in here another quote that has a text that is different than the quotes above in our state before. We have an author, and a new ID, as well as a new like count. Now if we go down to our expected value, this array here of quotes we're expecting, because we added this quote, we want our expected array to be the two quotes from our state before, added with this new quote here.

[05:02] So we should expect to get all three of those quotes in our expected array. So I'm going to go here inside of the array and add those three quotes. Now in our expected array we have the initial two quotes from the stateBefore function, as well as our new quote on the bottom. It is a good idea to test edge cases in your reducers, so I'm going to do one more test where we test to make sure like counts can't be negative.

[05:25] Here at the bottom I'm going to add a new test, a new it block that's going to say that it should return previous state when trying to make a quote like count negative, and then we will give that a function, and I'll set up the same template we've used before here. We'll have an action which is going to be an object, and then we'll set up our actual value which is going to be our quote reducer with the stateBefore function, and our action.

[05:53] Then we'll have our expected value which in this case is just going to be what we get from calling stateBefore, because we want our reducer to return the previous state when we're trying to make the like count negative. Then finally we set up our assertion that we expect our actual value to equal our expected value. Now let's go fill out our action, and it's going to have a type of unlikeQuoteById, and for our payload, we just need to pass it an ID.

[06:21] Let's use the quote with an ID of 2, because if we go up above, we know that from our stateBefore the quote with ID of 2 has a 0like count when we call stateBefore. That means that when we try to unlike this like count it should return 0instead of -1. That is how you can write tests for your reducers in redux.

~ 7 years ago

Just curious as to the reason why stateBefore needs to be a function rather than a const. Is that intentional?

Trevor Miller
Trevor Millerinstructor
~ 7 years ago

No reason, a variable would work great :)

~ 6 years ago

solid series!

~ 6 years ago

@Trevor good course! and a much needed one, thanks.

Think needs to be updated for Enzyme. Also reducers should not mutate the state. The current tests are not checking and enforcing the no mutation of state. You need to perhaps assign the results of the function call to a const and then deep freeze the const.