One of the first things I learned in my software engineering bootcamp is not to write code just to pass the tests. Just because I could spend a half hour tweaking a conditional statement to pass an RSpec test included in a project repository didn’t mean I understood how to solve the problem I was given. Oftentimes, really grappling with a difficult problem that you could maybe solve for only one specific case helped me more as a developer than getting all green tests. So it was a pleasant surprise to see how I had grown as a developer when I started writing my own tests first, before writing code to pass the tests. While the bootcamp I attended preached the virtues of TDD (Test-Driven Development), we were never taught how to write our own tests to begin with. So after I finished my bootcamp, and looking to work on some new projects, I decided to start learning the React Testing Library and Jest in order to incorporate TDD into my development process.
Test-Driven Development is the process in which a developer writes tests before writing the code to pass those tests. Often called “red-green” testing, the tests fail, or are red, before the code is written; the code is then written according to the “spec” set by the tests, which turn green when the tests pass. The advantages in following the TDD process are numerous: by integrating test-writing into the development process it no longer becomes a chore you have to do at the end, you can re-run tests after each update to your project to ensure everything is still working as expected, and writing tests allows you to pre-plan or pseudocode your solution before you start writing your code.
The React Testing Library was an appealing library to use to incorporate tests into my code because it has out of the box support for projects created with Create React App. The React Testing Library works by creating a virtual DOM for testing and utilities for interacting with it. We can fire click events and find elements on the page by their label text, just like a user would, all without having to open up the browser. The React Testing Library’s philosophy is that tests should resemble the way your software is used, the way a user will, “which allows your tests to give you more confidence that your application will work when a real user uses it.” Jest, on the other hand, is a test runner; it finds the tests, runs the tests, and determines whether or not your tests pass or fail.
Here’s an example of a simple test which determines whether or not a button changes color and text content when it is clicked:
As we can see above, the test syntax takes the form of a function. The first line of our test includes a render method which creates a virtual DOM for whatever JSX we give it as the argument — here we have the JSX for the App component. We can access elements on the virtual DOM via the screen global object. Because we want to determine whether or not a button on the DOM changes color when clicked, we first have to find the button; we can find the button by finding it by its role as a button using the getByRole method. After we find the button we can use a Jest-DOM assertion, which on the next line begins with the expect method and takes an argument, or the subject of the assertion. We expect the button to have an initial style of red, so we use the matcher of toHaveStyle and set the expected background color to red.
To test whether or not the button changes color when clicked, we can trigger a click event using fireEvent.click on the button; this click event is followed by another Jest-DOM assertion, which expects the button to have the style of blue. Finally, we need to test if the text content of the button changes relative to the styling changes we have coded for it, so we provide another assertion expecting the text content of the button toBe ‘Change to red’.
One of the most striking things about the grammar of Jest assertions and the React Testing Library syntax is how clear and easy to read it is. Even if we knew nothing about Jest or the React Testing Library, we could probably make a pretty educated guess as to what this test is looking for in our code. This brings me back to my initial point about TDD and the importance of writing tests before you code, it’s just like writing pseudocode. Writing tests forces you to think about the way elements on the DOM should work and the way users will interact with your application. Writing tests first has made me a better developer because I’m more methodical and organized in planning and writing my applications.