There are a lot of discussions in the web when we talk about testing. And even more about unit testing. In React.
This is going to be a brief summary of what and how should we think about when we test a ReactJS application. More specific, what should we write tests for, when creating React components for our app. If you want to skip the reading, here you can find shorter version in a few slides.
All of the points below are recommended by people smarter than me. I just collected them as a summary of best practices, trying to help my team to decide on the matter. And start using them in our day-to-day work.
Do we need unit tests?
This is a long time discussion, that has only one right answer. Yes! Tests provide developers with confidence. This confidence allows us to produce software with better quality. To do proper refactoring. And to reason better about what we do.
In short term it might look it’s not worth it. That it takes too much time. Time we might spend for implementing features instead. But very soon after the start of every project, the payoff of having a nice test suit is huge. The punishment of not having such, especially when a release date is approaching — disaster!
Test render logic
When we write tests for our components, we write them in such way that they test only the render logic and don’t care about any internal implementation. What does this mean? It means that, your test should be testing the things that the user will be seeing and interacting with. Let’s say you are testing a component that displays a button on the screen.
When the user clicks the button, your component renders a message in a div element. Your test should check practically for the same — check if the button is clickable and if this div exists after the clicking.
No lifecycle methods
Tests should not test lifecycle methods, they are supposed to be covered by React itself. If we have some logic that needs to be tested in these methods, we should try to extract it in another testable place and only use it in there. Ideally, in our componentDidMount method for example, we should have only functionality for fetching data (calling an API for example). All the rest that you might be tempted to leave there, should be extracted in other methods, that could be tested, if needed.
(Unit) Tests should not test interaction with outside world!
The name “unit” implies independence and autonomy. A unit test should be an encapsulated piece of code, that has inside everything it needs to perform its job. When we need to write tests which need data, we use mocks. That said, every interaction with APIs, DBs, etc., is not a matter of unit tests and should not be tested in such.
Small, smaller, easier
Tests should be small and easy to read — if we need to test a big component with a lot of dependencies, we need to consider splitting it to smaller testable pieces.
Dīvide et imperā.
As the famous quote above implies, it’s a lot more easier to deal with small, well modularized components, than the opposite. Also, if we need to use big mocks, we need to extract them out of the test and only use them by importing, in order to avoid polluting the test file.