A few years ago saying that frontend code required tests would cause many developers to start laughing. Today in mid-2019 not writing tests for your frontend code will cause even more laughter. I am pretty sure you have heard the term Test Driven Development
or TDD and it is a big requirement these days in order to get a job in the web development industry. What is that? Why should I do it?
TDD to many codes is a synonym of “oh shoot, now I have to write tests”. This is not far from the truth but there is a different approach that will help you become a much better developer. I won’t get too deep into TDD itself but its main philosophy is that you first write your tests and then write your code. The idea is that the tests drive the development of the code. In other words, you first write the tests with the required scenarios you are working on and then write your code which should eventually pass all the tests. Simple right? Waaaait there’s a lot more.
When I started learning about TDD I found that applying it into React was very frustrating. That was because React components have way too much logic inside each component to be tested. How do I test the component life cycle methods? How do I mock api calls? What about state management? There were many more questions that made me look at TDD as a “good, but no one has time for it” approach. A few months later, I changed my job and I started working in a much bigger team. That was where I noticed that I was looking at TDD in the wrong way.
The big mistake I was doing before that new job was that I was looking at React components as one single and gigantic piece of code. This is where I was introduced to the term “seperation of concerns” which is a beautiful concept about splitting our component in several pieces depending on its purpose. Some examples are the actual JSX part, its styling (quite handy if you use styled-components), typescript models, constants, test data, helper functions, api calls and many more. That way we don’t have to test the component as a whole. Each different piece can be tested independently. “Cool story bro but you still haven’t talked about testing the React component itself.”
I am personally using react-testing-library from Kent C. Dodds which is one of the simplest ways to test React components. Let’s refer to it as RTL in the rest of the post. The way I write my React tests is that I first start with a snapshot that uses the lowest amount of props required for the component to be rendered. A snapshot is like a serialised value of how your component looks like. A snapshot is how your component looks and behaves as you are currently testing it versus how it was when the snapshot was initally created or changed. I find snapshots really useful because they give you a heads up about changes that affect components which you didn’t realise they would change. In other words, if you changed an input component, now all the wrappers or pages that contain this input will change and the code will complain about a non-matching snapshop. In these cases we just update the snapshot and we are good to go. Snapshots personally help me with components that I didn’t think that would be affected by changes I did. That makes me double check that no other components broke unexpectedly. An example of a snapshop is.
import React from "react"
import { render } from "react-testing-library"
const Wrapper = children => <div style={someAwesomeStyle}>{children}</div>
test("should match snapshot", () => {
const { container } = render(<Wrapper>Hello world</Wrapper>)
expect(container.firstChild).toMatchSnapshot()
})
Testing the props in most cases is quite simple. The difficult part is testing the behaviour of the component. The first thing we want here is to be able to select each specific element within a component. To do that RTL has provided us with some really cool tools that allow searching and selecting parts of the component and then make sure they contain the properties we expect them to have. RTL contains plenty of queries we can call. Some of them are getByText
, getByTestId
, getByAltText
and more. If you are not sure if the element exists the query
convention does a great job like queryByText
, queryByDisplayValue
etc. In case there are more than one matching elements the all
convention can be very useful, example getAllbyAltText
, getAllByRole
etc. Let’s now see an example. Our react component is like:
import React from "react"
import { render, fireEvent } from "@testing-library/react"
const Button = ({ onClick, children }) => {
return (
<button data-testid="my-button" onClick={onClick}>
{children}
</button>
)
}