This project demonstrates ways in which we can test various aspects of our React code, using Vitest and the React testing library.
Vitest is a testing environment almost identical in API to Jest, but designed from the ground up to integrate easily with Vite-based projects. Using this instead of Jest, when working with Vite projects, is infinitely easier. The knowledge you've gained with Jest can be put to direct use as the API is almost identical - but the project setup is slightly different (see "Setup" section below).
The React testing library, built on top of the DOM testing library, essentially simulates a browser environment in which developers can "render" their components, and examine the ouutput "on screen", in terms of which HTML elements have been rendered, along with their contents / attributes. We can also simulate user input (e.g. button clicks), and verify that our components behave as expected.
The core function is the render()
function, into which we can supply our component hierarchy we wish to test. The function returns several functions we can use to examine and interact with rendered content. For example:
- queryBy*** functions: return either the matching component, or
null
if there are no matches. - getBy*** functions: return either the matching component, or throw an exception (causing the test to fail) if there are no matches.
- findBy*** functions: return a promise which will resolve to the matching component if one is found within a given timeout (default 1000 milliseconds), or reject otherwise.
Variations of these functions include the ability to query by text content, role, or several other factors. The full list of query functions is available here.
In addition to query functions, we can import a fireEvent
object that can be used to simulate user input (e.g. button clicks), and a waitFor()
function which can be used to wait for certain events to occur / conditions to be met (via a Promise). Using a combination of all of these functions, we can comprehensively test our React code.
This section explains how to setup Vitest and the React Testing library in your Vite+React project, and how to use them in your unit tests.
- Firstly, let's install:
npm install --save-dev jsdom vitest
- Next, we will add a "test" script to our
package.json
'sscripts
prop:
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest"
},
- Now, we'll modify our
vite.config.js
file with a section that configures Vitest (thetest
property you can see here):
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom"
}
});
- With that, our Vitest config is done. However, unlike with Jest, we'll need to
import
the various testing functions, such asit
andexpect
, at the top of each of our test files, like so:
import { describe, expect, it } from "vitest";
- To install the testing library, we need to install some dev dependencies:
npm install --save-dev @testing-library/react @testing-library/user-event @testing-library/jest-dom
The first of these dependencies (.../react
) is compulsory for using the testing library with React apps. The second (.../user-event
) allows us to easily simulate user input (such as clicking buttons) on our components under test. The final (.../jest-dom
) allows "nicer" testing expect()
statements such as ...toBeInTheDocument()
, which can check if an HTML element matching some given criteria is present in the rendered HTML.
- If you're using
@testing-library/jest-dom
, you'll also need to import it at the top of each of your test files, like so:
import "@testing-library/jest-dom";
business-card.test.jsx
shows some basic unit tests for a BusinessCard
component. We can see the use of the render()
function here, as well as several query functions (queryByText()
, getByRole()
, getByText()
).
component-with-context.test.jsx
shows how we can test a component which requires the use of context (i.e. obtains some values using useContext()
). We can surround the component under test with a dummy context provider, which supplies dummy data via the context mechanism.
component-with-routes.test.jsx
shows how we can test a component which contains React Router components such as Link
, NavLink
, Routes
, and Outlet
. If we render any React Router components, we must surround our components under test in some kind of Router, or we will get errors. React Router provides the MemoryRouter
component which works really well for this purpose. It provides an initialEntries
prop which we can use to supply the initial simulated browser history stack, thus being able to "start the app" at any particular URL we desire. In these tests, we use this functionality to check whether the correct components are being rendered when the user navigates to particular paths.
greeting-loader.test.jsx
shows how we can combine axios mocking with axios-mock-adapter
, with our React testing code. In this test, we're examining the GreetingLoader
component, which should load a greeting from a web API and display it, when we click a button. We we are simulating that button click, then using axios-mock-adapter
to return a dummy web response to our component, then ensuring the content contained within that response is correctly rendered. Note: For another example involving axios-mock-adapter
by itself, refer to example 23.