Source Code available at Github
In this article
- In this article
- Introduction
- Why Vitest?
- Setting up the Test Environment
- Testing Component Behavior
- Conclusion
Introduction
Unit testing is a crucial practice in software development that involves evaluating individual units of code to ensure they function correctly. It forms an integral part of Test-Driven Development (TDD), a methodical approach that emphasizes continuous testing and revision during the development process. In this article, we will explore how to utilize Vitest, a modern testing framework powered by Vite, to rigorously test React components. Our primary goal is to validate the functionality of our code and detect potential bugs early in the development cycle.
Why Vitest?
Vitest stands out as a cutting-edge testing framework, benefiting from the speed and efficiency of the Vite build tool. It offers a zero-config setup, simplifying the testing process for React components.
To follow along with this tutorial, you can clone the Starter Project and switch to the before-vitest
branch using the following commands:
git clone https://github.com/Abhi0498-Blogs/vitest-react.git
cd vitest-react
git checkout before-vitest
npm i
npm run dev
This will initiate the development server, accessible at http://localhost:5173.
Begin by installing Vitest and @testing-library/react:
yarn add -D vitest @testing-library/react
Now, let's configure our vite.config.js
file as follows:
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
},
});
In this configuration:
-
globals
makes all variables available in test files. -
environment
sets the environment tojsdom
, which is the default environment for Vitest.
To manage testing, update your package.json
scripts:
// package.json
{
"scripts": {
"test": "vitest",
"test:watch": "vitest --watch",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
}
These scripts facilitate various testing scenarios:
-
test
: Run all tests. -
test:watch
: Execute tests in watch mode. -
test:ui
: Launch the Vitest UI. -
test:coverage
: Generate a coverage report.
Setting up the Test Environment
Create a __tests__
folder within the src
directory to house your tests:
mkdir src/__tests__
Now, let's take a closer look at the src/components/buttons/
folder, which contains four components: IncreaseButton
, DecreaseButton
, IncreaseBy10Button
, and DecreaseBy10Button
. We'll begin by testing the IncreaseButton
component.
Create a new file called IncreaseButton.test.jsx
within the __tests__
folder:
touch src/__tests__/IncreaseButton.test.jsx
Now, let's start writing our first test:
// src/__tests__/IncreaseButton.test.jsx
import { describe, expect, test } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import IncreaseButton from "../components/buttons/increase";
import { useState } from "react";
describe("IncreaseButton", () => {
test("renders", () => {
render(<IncreaseButton />);
expect(screen.getByText("Increase")).toBeDefined();
});
});
In the code above:
-
describe
is used to group tests together. -
test
is used to define a test case. -
render
renders the component. -
screen
provides access to DOM elements. -
expect
is used for assertions.
By executing this test, we ensure that the component renders without errors. If the "Increase" text is found in the DOM, the test passes; otherwise, it fails.
Now, let's run the test:
npm run test
If the test succeeds, you'll see a result similar to this:
This represents a test case—a set of conditions used to validate the system under test (SUT). Additionally, it highlights the importance of testing, as any future changes to the component that alter the text "Increase" will trigger a test failure.
Testing Component Behavior
Let's write a test to verify that the count increases when the button is clicked. To achieve this, we'll add another test within the describe
block:
// src/__tests__/IncreaseButton.test.jsx
test("should increase count by 1", () => {
// Arrange
const ButtonWithCount = () => {
const [count, setCount] = useState(0);
return (
<>
<div data-testid="count">{count}</div>
<IncreaseButton setCount={setCount} />
</>
);
};
// Act
render(<ButtonWithCount />);
const count = screen.getByTestId("count");
const button = screen.getByText("Increase");
fireEvent.click(button);
// Assert
expect(count.textContent).toBe("1");
});
In this test:
- We create a component called
ButtonWithCount
, which includes a count state and theIncreaseButton
component. - The
render
function rendersButtonWithCount
. -
fireEvent
is used to simulate a click on the button. -
expect
is employed to assert that the count has increased to 1.
Now, let's run this test using the Vitest UI:
npm run test:ui
You'll see the Vitest UI, where you can execute your tests:
Running tests in the UI offers a visual and interactive approach to testing your components.
Conclusion
In this tutorial, we've covered the fundamentals of unit testing using Vitest and how to test React components effectively. Remember that this is just the beginning, and there are more components in the src/components/buttons/
folder that you can test to gain a deeper understanding of Vitest.
By writing tests for your code, you not only ensure its reliability but also catch potential issues early in the development process. Embrace testing as a vital practice in your software development journey.
Happy Coding! 🎉
Top comments (3)
Great article. Didn't now about ui. Really cool stuff.
I created React/Typescript/Vitest starter template based on this, if anyone interested. Welcome any improvement too -
github.com/monaye/vite-vitest-reac...
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@vitejs/plugin-react-swc'
^^ This package needs to be included when the dev deps are installed at the beginning.
This article is very helpful. Thank you for sharing.