React Tests — Component Tests, Async Action Tests, and Reducer Tests

Tiago Gomes
Runtime Revolution
Published in
8 min readNov 6, 2018

--

Since I started developing I have always heard about testing my applications, but we would never spend much time on it. This started in college where there were some classes that gave an idea of what to do but never focused on the subject. The same is true in the market. The client wants his money well invested and wants to see things happening. This is even truer in front-end. There is always a rush to deliver features but that often comes at the expense of clean code and mitigating future errors. Having this in consideration I decided to create a small overview of libraries and some methods to test your application.

Have thought of developing your React Application using Test Driven Development (TDD) or your client wants the project to have tests? If you don’t know where to start this is a good starting place!

First things first I will create a project with create-react-app and add the following packages:

  • enzyme (to make a shallow rendering of the components)
  • react-test-renderer (to test snapshots)
  • redux, react-redux, and redux-thunk (because we will test the actions and the reducers)
  • redux-actions, (simplify how to create the actions and how to handle them on the reducer)
  • redux-mock-store (to mock our redux store, obviously)
  • jest-fetch-mock (to simulate the different fetches)

To better understand how the process will go, we will create an app that gives an advice either randomly or based on an input.

For this, we will use this API http://api.adviceslip.com/ to help generate those helpful pieces of advice.

First things first you will need to create a simple app, you can download here the initial skeleton without any kind of tests.

Ok, so now we have our skeleton created and we want to start working on those tests. First things first, we will start with the redux actions.

These tests can be divided into two categories: actions and async actions. The first set of tests even if they aren’t very useful, are to test the object generated given to the dispatch. Since we are using the redux-actions this will prevent some errors if you do it explicitly.

Actions

So here is our action creator, located on the file Action.js:

Now for testing this code, first thing is to create a file normally with the same file name as the Action but with the “.test” extension, so in this case, it will be Action.test.js. Having created the file we will import all our actions into the file. And then call the describe function to know which section of the tests we are creating. After this, we call the ‘it(description, function to test code)’ function, where the first argument is a string to identify the test, and the second is a function where we will test the code. Check the following example to understand how to do it:

So as you can see we created the expected action that should be generated by the call of our action. These tests aren’t very useful unless you want to validate arguments, but here is another test we can add to our file:

As you can see in the test above we are expecting that when we call the action success with the object advice, it should have it in its payload. This is important while developing in case the API changes or you updated your actions but forgot the reducer. This way you will know why the reducer is breaking.

Async Actions:

These actions tests aren’t the most useful nor that helpful except in very specific cases. There are actions tests way more useful that will help you make sure your app will not break with different answers from the API. These are called async actions. These actions will have a promise to resolve and might have more than one action. For example, if you make a request, you might use three different types of actions: a request, a response failure, and a response success action. This can be helpful when your app needs to change its state while fetching data.

So let’s start working on this again.

The first thing to do is import the jest-fetch-mock, to mock our API calls, and the redux-mock-store to mock our store for each test. Once this is done, we will initialize each variable, with the mock data we need, and then we will start the test.

As you can see in the example above, to test our async actions we started by calling “mockResponse” with an answer. This will override each fetch called on your test. Once that is done, we created the expected actions by calling getRandomAdvice. After this, we will dispatch that function to our mocked store and then test if the generated actions are the same as the expected actions.

This test seems simple enough and only tests if everything is working well but what about the case where the API fails? What if you want to give different answers to the user depending on the status code? Well for those cases there is a library that can help by defining the type of status code or send an error through the fetch to check what should happen in a failed case. That library is called jest-fetch-mock.

Here are three tests where the last two show these cases. First is when the server is down. The second test is when something unexpected happens and an error is thrown.

These two examples show how simple it is to test several types of answers and help you prepare your async actions for any kind of answer. Once you have this, you can check what the behaviour of your program is and what should happen in case it fails.

Reducers:

Having seen all unit tests we can do for the actions we will now focus on the reducers. Reducers are responsible for saving the information on the store and if you don’t treat it correctly you might catch unexpected values on your application. Because of this, reducers tests are fundamental to make sure your app is behaving as expected.

The very first thing we need to do to start working on the Reducer’s tests is importing the Reducer we want. After this, we will create a test for each action.

For example, if we apply the action “REQUEST_ADVICE” to a state, in this case, this state will have, ‘fetching’ to false and “randomAdvice” to null. We hope that a new object will be returned with the fetching variable as true and the “randomAdvice” with the same result as the previous state. This should always happen and this way we can verify if the action is mutating correctly to the new state.

One thing to note is you don’t need to create all the variables in your state. If an action just affects some variable you can simply use it as shown on the “RESPONSE_ADVICE_FAILURE” test.

Also if your action will have a payload we can simply add it to the action, and then if you use it we expect it to show on the expected State as shown in the test related with the response_advice_success.

This part covers all the unit tests which cover your redux actions and reducers. Now we will focus on the component tests which check if the behaviour of our app is the correct one or not.

Component Tests:

Having tested everything related with Redux we are left with testing our components. In this section, we can test the functions of a component and the snapshots to check if, given some props, the component renders the expected structure.

So as you can see down below, or in the project GitHub, we have our main application component. It has one input box and two buttons, one to find the advice based on the input value and the second to give a random advice. So here we can see three methods one of them is defineLabelAdvice which returns the label depending on if you have a value or not.

To start creating the method tests we need to import the shallow function from the enzyme. The shallow function will simulate the element. So if you try to debug it you will see the HTML tree it generates, I left the console log there if you want to check it out. Once you have your wrapper for the component you can start testing it. To test the methods in it you need to call the instance method to give you an instance of the component with all the methods available in it. That way you can call each method and test it for different input and several states.

So as you can see down here we have a test to check if the method “defineLabelAdvice” will return the correct string. When we call it we expect the return value to be ‘This is a random advice” because it does not have any value in the inputs. But this wouldn’t be a good enough test if you didn’t test the function for each case. That is why I called the setState function to update the state and called the function once again, and as you can see the expected label should be different.

Once you have finished checking these tests, you will also notice that we have snapshots to check if this advice is being used on the HTML. If some condition changes, the snapshots might fail and, in some cases, you should check them before updating them. With the snapshots you are testing the HTML generated by that component. I did it in each test because this way we are also testing if the correct label is being displayed. And this covers part of the component test. You can also use functions like “children” in the shallow result to check how many elements you have. This is useful for lists or if you generate code from maps. You can check other examples in the enzyme documentation https://airbnb.io/enzyme/docs/api/

To wrap-up our tests we need to know our code coverage. You have two ways to do this. Either you install a package or, if you are using react-create-app, you can use the built-in scripts. To access them you need to eject your project (Check this link to understand what it does) and add the following line to your package.json:

“coverage”: “node scripts/test.js — env=jsdom — coverage”,

Once you had it, you can run the coverage by using “npm run coverage”. This will generate a folder called coverage with a report inside it.

At Runtime Revolution we take our craft seriously and always go the extra mile to deliver a reliable, maintainable and testable product. Do you have a project to move forward or a product you’d like to launch? We would love to help you!

--

--