React.js Unit Testing with Jest
React.js Unit Testing with Jest
In this article you will learn how to do unit tests in a React js application. I will try to explain for you the main reasons to perform unit tests and how to write test cases.
Happy learning and happy coding!
Building a well-functioning application requires good testing; otherwise, knowing whether your application works as expected would be a matter of guesswork and luck.
According to Michael Feathers, any piece of code that has no tests is said to be legacy code. Therefore, one of the best ways to avoid creating legacy code is using test-driven development.
While there are many tools available for JavaScript and React.js unit testing, in this post I will be using React.js testing tool named Jest, along with the popular library Enzyme, which is designed to test React components.
Why Use a test driven approach to Create a React.js Component?
If you have created a React.js component before, you’ve realized that code can grow really fast. It fills up with lots of complex conditions caused by statements related to state changes and service calls.
Every component lacking unit tests has legacy code that becomes difficult to maintain. By creating tests, we have a higher chance of covering every logic scenario in our component, which would make it easy to refactor and maintain.
Introduction To Jest
Jest is a delightful JavaScript testing framework with a focus on simplicity. It can be installed with npm or Yarn. It works great for React applications, but it also works great outside of React applications.
Enzyme is a library that is used to test React applications. It’s designed to test components, and it makes it possible to write assertions that simulate actions that confirm whether the UI is working correctly.
Process Of Running A Test With Jest
Let’s install Jest and start writing some tests!
1. Create a new directory,
mkdir react-jest
2. Initialize your react app,
npx create-react-app jest-test
3. Next up install necessary libraries. With npx command we already have, @testing-library/jest-dom and @testing-library/react. Now we need a library called react-test-renderer.
npm i react-test-renderer –save-dev
4. By default, Jest expects to find test files in a folder called __tests__ in your project folder.
5. Jest will recognize any file with the suffix .spec.js or .test.js.
Before we start writing our tests, there are some key things that you’ll need to understand.
# it or test You would pass a function to this method, and the test runner would execute that function as a block of tests.
# describe This method is for grouping any number of it or test statements. Every time you start writing a new suite of tests for a functionality wrap it in a describe block.
# expect This is the condition that the test needs to pass. It compares the received parameter to the matcher. It also gives you access to a number of matchers that let you validate different things.
# mount This method renders the full DOM, including the child components of the parent component, in which we are running the tests.
# shallow This renders only the individual components that we are testing. It does not render child components. This enables us to test components in isolation.
Let’s create a Button component
1. In src folder create mkdir components and inside components mkdir Button (This is just the structure that React deevlopers follow).
2. Create Button.js, button.css and a folder __tests__ where we will be writing our test component.
3. The Button component accepts a label as a prop and returns a div.
<div data-testid=”button” className=’button-style’>
{label}
</div>
4. Let’s import this Button component to our App.js as pass a prop label to it.
<div className=”App”>
<header className=”App-header”>
<Button label=’Click me please’ />
</header>
</div>
5. In the __tests__ folder, create Button.test.js file. To begin with, let’s write a basic test to cheeck if the Button component renders successfully.
it(“renders without crashing”, () => {
const div = document.createElement(“div”);
ReactDOM.render(<Button></Button>, div)
})
6. Now that we have our first test case, let’s start this with npm test. We get the following result that we get in the terminal:
PASS src/App.test.js (8.903 s)
PASS src/components/Button/__test__/Button.test.js (9.883 s)
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 27.874 s
The result says it passed 2 tests, this is because create-react-app comes with a default test for our App component. Now anytime any changes are made, the test will re run to check those changes.
7. Let’s have one more test from render which comes from @testing-library/react. Once we render the Button, we can get multiple things from this. We added a data-testid to our element, so we can access this element using data-testid. The render returns a function called getByTestId. So we write this function and pass our testid and wrap this function with expect which comes from @testing-library/jest-dom/extend-expect. Here we are expecting to toHaveTextContent “click me please”, meaning that if we get this button by the data-testid, it should have “click me please”.
it(“renders button correctly”, () => {
const { getByTestId } = render(<Button label=’click me please’ ></Button>)
expect(getByTestId(‘button’)).toHaveTextContent(“click me please”)
})
The result that we get says that 3 tests have passed successfully.
Test Suites: 2 passed, 2 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 4.69 s, estimated 13 s
A Fail Test
Let’s also check for a fail test.
it(“renders button correctly”, () => {
const { getByTestId } = render(<Button label=’click me please’ ></Button>)
expect(getByTestId(‘button’)).toHaveTextContent(“save”)
})
Here, the test fails, beccause the element is expected to have text content “save” but instead it received “click me please”.
Cleanup after each test
After every test, it adds the element to the DOM tree. So after every single test we need to cleanup so that all the residue won’t be left and there can be multiple different kind of tests. Cleanup comes from ‘react-testing-library’ and in the begining of writing the test just add, afterEach(cleanup).
Snapshot Testing with Jest
Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly. A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the UI component.
Snapshot looks for a folder called __snapshots__ inside of the foldr __test__ and it creates a file for us inside the folder when you run it for the first time.
it(“matches snapshot”, () => {
const tree = renderer.create(<Button label=’save’></Button>).toJSON();
expect(tree).toMatchSnapshot();
})
The output from the snapshot file (button.test.js.snap):
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`matches snapshot 1`] = `
<div
className=”button-style”
data-testid=”button”
>
save
</div>
`;
Now if we change something in our Button component, the test should fail. It is because a snapshot is saved and the definition of test is not changed. So, on making any changes, it will save a new snapshot, which will not be same as the one saved earlier.
It gives you a choice, stating that if you intentionally changed something and you know that it is a right change, you can update the snapshot. In the terminal you will see the list of commands which will help to update your snapshot. After updation, now the requirements are met and the test will pass.
Conclusion
We are at the end of our exploration of adding tests to a React application. In this article we learned about few basic test that we can perform with Jest. I’ve introduced you to the process of running a test, testing React components, mocking and Jest has much more to offer.
Leave a Comment