When building a website and integrating diverse features, manual testing may appear viable at the outset. However, for larger projects, relying solely on manual testing becomes impractical prompting the need for the implementation of unit tests. Not only do unit tests enhance the integrity of your application, but they also provide a more scalable and efficient approach to ensuring the functionality of your codebase.
What is unit test?
Unit testing is a testing methodology that assesses individual software units in isolation. It involves scrutinizing the output of a function for any given input. This process ensures the verification of component rendering for specific inputs to React components.
In essence, writing unit tests is simply writing code to ascertain the code works as expected.
In this article we are going to be looking at the fundamentals of react unit testing ,exploring essential concepts and structures.
Both Jest and React Testing Library come pre-packaged with Create React App and adhere to the guiding principle that testing apps should resemble how the software will be used.
Prerequisties
- Npm and knowlegde on node and javascript
- Familiarity on react
In testing, the focus is on ensuring that your code performs as intended in comparing expected and actual outputs. The major aspects to test in your code include:
Rendering with or without props: Check if a component renders correctly with or without specific props.
Rendering with state changes: Test how a component renders when there are changes in its state.
Reacting to user interactions: Assess how a component responds to user interactions.
However, certain elements do not require testing:
Actual Implementation: We don't need to test the exact implementation, we just need to ensure component produces the expected output.
Third-Party Libraries: If utilizing third-party libraries like Material UI, there's no need to test them, as they are presumed to be well-tested.
In react unit testing , there's a conventional test block structure that is followed, which is :
- Create a test block using either describe-it/test.
- Inside the test block, the first thing we do is to render the component that we want to test.
- Select the elements that we want to interact with queries.
- Interact with those elements.
- Assert that the results are as expected.
How To Setup Our Project
First create the react app with the create-react-app command. Since we'll be using Jest , its already pre-installed in your app.
npx create-react-app react-test-project
Ensure the following line is in the setupTests.js file:
import '@testing-library/jest-dom'
How To Write Our First Test ?
Step 1: Create a component
Let’s create a component called Counter, that renders "Counting App".
import React, { useState } from "react";
const Counter = () => {
return (
<div>
<p>Counting App</p>
</div>
);
};
export default Counter;
Here, we just need to test if the Counter renders "Counting App". Now, where should we write the tests? We can write them inside files with .js suffix in “__ tests __” folders or files with “.test.js” suffix or files with “.spec.js” suffix anywhere inside our src folder and jest will pick it up.
Create a “counter.test.js” file inside the src folder and add the following code:
import Counter from "./counter";
import { render, screen } from "@testing-library/react";
describe("Counter Component", ()=> {
it("should render counter component", ()=>{
render(<Counter />)
expect(screen.getByText(/counting app/i)).toBeInTheDocument()
})
})
Note: In order to let jest know about this test file, it’s important to use the extension .test.js.
The above test can be described as follows:
- The “describe” block is employed to group together a set of tests under the overarching category of "Counter Component."
- The “it” block is used to define individual tests within the describe block. Alternatively, the “test” block can also be used for the same purpose. Both methods take two parameters:
- The first parameter names the test, such as "Render Counter Component."
- The second parameter is a callback function, outlining the specifics of the actual test.
- The render() method from the React Testing Library is utilized in the test to render the Counter component within a virtual DOM.
- The screen property, offered by the React Testing Library, facilitates the selection of elements for testing based on the previously assigned test IDs.
- The expect property is used to make assertions, comparing the actual results with the expected results.
With these foundational concepts in place, the testing process covers the basic rendering aspects. The subsequent step involves implementing counting functionality in the Counter component.
import React, { useState } from "react";
const Counter = () => {
const [countValue, setCountValue] = useState(0);
const increaseCount = () => {
setCountValue((prevCount) => prevCount + 1);
};
const decreaseCount = () => {
setCountValue((prevCount) => prevCount - 1);
};
return (
<div>
<h2>Counting App</h2>
<button
data-testid="increase"
aria-label="Increase Count"
onClick={increaseCount}
>
+
</button>
<p data-testid="count">{countValue}</p>
<button
data-testid="decrease"
aria-label="Decrease Count"
onClick={decreaseCount}
>
-
</button>
</div>
);
};
export default Counter;
This component that simply increases and decreases a numeric value at the click of respective buttons. Now our updated test file should look like this:
import Counter from "./counter";
import { fireEvent, render, screen } from "@testing-library/react";
describe("Counter Component", () => {
it("should render counter component", () => {
render(<Counter />)
expect(screen.getByText(/counting app/i)).toBeInTheDocument()
})
it("should increment counter when increment btn is clicked", () => {
// render the component on virtual dom
render(<Counter />);
//select the elements you want to interact with
const counter = screen.getByTestId("count");
//expect counter to initialize at 0.
expect(counter).toHaveTextContent("0");
//select increment btn
const incrementBtn = screen.getByTestId("increase");
//interact with those elements
fireEvent.click(incrementBtn);
//assert the expected result
expect(counter).toHaveTextContent("1");
})
})
In the second test block, we are evaluating the rendering and increment functionality of the counter component. Here's a breakdown of the test:
- Render the counter on the virtual DOM.
- Utilize the screen property to select elements.
- Set an expectation assertion to check if the counter initializes at 0.
- Use the fireEvent function to simulate a click on the increment button.
- The test asserts that the counter's text content changes and updates as expected.
Now after running the test, the result obtained can be seen below:
PASS src/counter/counter.test.js
Counter Component
√ should render counter component (39 ms)
√ should increment counter when increment btn is clicked (22 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.188 s
Ran all test suites matching /counter/i.
The assertions confirm that the Counter component not only renders as anticipated but also functions correctly when incrementing. I trust these tests have provided a foundational understanding of exploring basic rendering and interactive features within a React component using the React Testing Library. However, this marks just the initial phase of our testing journey ,as you continue your journey into the world of testing, the next step involves exploring more advanced techniques. My upcoming article Mocking Axios GET Requests, will dive deeper into the critical aspects of mocking and asynchronous testing. Make sure to follow along for a comprehensive understanding of these advanced testing concepts.
Stay focused, and happy testing!
References
Top comments (0)