Unit testing in react
Test types and approaches
Unit testing: Testing individual modules or components of the program for correct
operation. Unit tests are usually written and executed by developers to check specific
functions or methods. Such tests are generally quick to write and can be executed
quickly, but they do not test the final application for critical bugs, as the tested and
stable components themselves may have problems when interacting with each other.
An example of a unit test would be checking the functionality of a single function, React
component, or Hook.
Integration testing: Testing in which we check the interaction between various modules
or system components. The goal is to detect defects in the interfaces and interactions
between integrated components. This type of testing is usually conducted on the server
side to ensure that all systems work smoothly together and that the business logic
meets the specified requirements
For example, an integration test would be one that checks that user registration works
by making real calls to REST API endpoints and checking the returned data. Such a test
depends less on the application’s implementation and code and more on checking
behavior and business logic.
End-to-end (E2E) testing: Testing a complete and integrated software system to ensure
that it meets specified requirements. E2E testing evaluates the program as a whole.
This type of testing is the most reliable, as it completely abstracts from the application’s
implementation and checks the final behavior by interacting directly with the application
itself. In the process of such testing, for example, in a web application, a real browser is
launched in a special environment, in which a script performs real actions with the
application like clicking buttons, filling out forms, and navigating through pages.
Unit Testing
Unit testing is the process of verifying the correctness of individual “units” of code:
namely, functions and methods. The goal of unit testing is to ensure that each separate
unit performs its task correctly, which, in turn, increases confidence in the reliability of
the entire application. In React, it ensures components behave as expected, catches
bugs early, and facilitates refactoring without breaking functionality.
Vitest and its key features for testing React
Vitest is a fast, modern test runner built by the creators of Vite. Vitest is a Vite-powered
test runner for fast unit testing. Features include Jest-compatible API, mocking,
snapshots, and concurrent test running, making it efficient for React apps.
Setting up a basic unit test for a React component using Vitest
import { render, screen } from '@testing-library/react';
// screen → lets you query elements inside the rendered component
import { vi } from 'vitest';
// Included since Vitest is the test runner
import Component from './Component';
// this component displays the expected text
test('renders text', () => { render(<Component />);
expect([Link]('Hello')).toBeInTheDocument(); });
// "renders text" describes what this test is checking. The render statement simulates
putting the component into a fake browser environment.
// [Link]('Hello') tries to find an element in the DOM that contains the text
Hello. .toBeInTheDocument() comes from @testing-library/[Link] asserts that this
element exists somewhere in the DOM produced by the [Link] it cannot find it → the test
fails.
Mocking in Vitest and its use in React tests
Mocking means replacing real functions, components, or modules with fake versions
during tests. Mocking replaces dependencies (e.g., API calls) with fakes. In React, we
use [Link]() or [Link]() to mock functions/modules, isolating tests from external factors
like network requests.
Setting up the test environment
Create the App
npm create vite@latest my-app -- --template react-ts
1. Install All Required Dependencies
Run this:
npm install -D vitest @testing-library/react @testing-library/jest-dom
@testing-library/user-event jsdom @vitejs/plugin-react
//Add @latest to install latest versions (Recommended)
npm install --save-dev vitest jsdom @vitejs/plugin-react @testing-library/react@latest
@testing-library/jest-dom@latest @testing-library/user-event@latest
2. [Link]
This is the official way to configure Vitest inside Vite projects.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true, // adds describe/test/expect globally
environment: "jsdom", // required for React Testing Library
setupFiles: "./src/[Link]",
css: false, // optional: disable CSS processing in tests
},
});
3. src/[Link]
This loads jest-dom matchers like .toBeInTheDocument().
import "@testing-library/jest-dom";
No need to import expect manually.
4. Add script in [Link]
{
"scripts": {
"test": "vitest",
"test:watch": "vitest --watch"
}
}
5. src/[Link]
A simple component to test:
export function App() {
return <h1>Hello world</h1>;
}
6. src/[Link]
A fully working test:
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { App } from "./App";
describe("App", () => {
it("should be in document", () => {
render(<App />);
expect([Link]("Hello world")).toBeInTheDocument();
});
});
7. Run the test
npm run test
Testing a custom React Hook
[Link]
import { useState } from "react";
export function useCounter(initialValue: number = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount((c) => c + 1);
const decrement = () => setCount((c) => c - 1);
return { count, increment, decrement };
}
[Link]
import { renderHook, act } from "@testing-library/react";
import { useCounter } from "./useCounter";
import { test, expect } from "vitest";
// renderHook allows you to run React hooks in a test, act ensures React updates
happen correctly, useState represents the hook we are testing and expect, test are
Vitest test utilities
// The test name explains what you are verifying.
test("useCounter", () => {
const { result } = renderHook(() => useCounter());
expect([Link]).toBe(10);
//It will return an error because the initial value was 0.
act() tells React: Such code updates state. You have to process updates before
continuing
act(() => {
[Link]();
});
expect([Link]).toBe(1);
act(() => {
[Link]();
});
expect([Link]).toBe(0);
});