Vitest runs tests in a Vite-based React project. Its API is almost identical to Jest’s, but it does not come bundled with Vite. Pair it with React Testing Library to test components through their rendered behavior rather than using the runner alone. section 10.0 section 10.1.1
To add component tests to a standard Vite setup:
- Install vitest and jsdom as development dependencies, alongside @testing-library/react and @testing-library/jest-dom. section 10.1.1
- Mark *.test.js and *.test.jsx files as a Vitest environment in ESLint so globals such as test and expect are recognized. section 10.1.1
- Set Vite’s test options to use globals and the jsdom environment, then add a package script invoking vitest. section 10.1.1
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: { globals: true, environment: "jsdom" }, #1
});
The Vite configuration supplies the test globals and simulated DOM needed by these component tests. section 10.1.1
Keep a component’s test beside it, such as MenuItem.test.jsx beside MenuItem.jsx. Arrange the render, act when interaction is needed, and assert the result. Test files run when tests are requested; they are ignored by the development run and production build. section 10.1 section 10.4
Listing 10.1 Unit-testing MenuItem
import { #1
render, #1
screen, #1
getByRole, #1
} from "@testing-library/react"; #1
import "@testing-library/jest-dom"; #2
import MenuItem from "./MenuItem"; #3
test( #4
"MenuItem renders a link in a list item", #4
() => { #4
// ARRANGE
render(<MenuItem href="/blog" label="Blog" />); #5
// ASSERT
const listItem = screen.getByRole("listitem"); #6
const link = getByRole(listItem, "link"); #6
expect(link).toHaveAttribute("href", "/blog"); #7
expect(link).toHaveAttribute("title", "Blog"); #7
expect(link).toHaveTextContent("Blog"); #7
});
The static-component test renders a menu item, finds its list item and link by role, then checks the link’s href, title, and text. section 10.1
For callbacks, vi.fn() creates a mock function whose invocation can be checked. In an interactive to-do test, a user click on the second item’s delete button must call the mock with that item. This verifies the callback contract without needing a real deletion handler. section 10.2.2
Listing 10.9 Testing the items component from a Todo app
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import { vi } from "vitest";
import { Items } from "./Items";
test("Items should call onDelete callback
when item is deleted", async () => {
// ARRANGE
const items = ["Item A", "Item B"];
const mockDelete = vi.fn(); #1
render( #2
<Items items={items} onDelete={mockDelete} />, #2
);
// ACT
const user = userEvent.setup();
const secondItemDelete = screen.getByRole("button", {
name: "Delete 'Item B'",
});
await user.click(secondItemDelete); #3
// ASSERT
expect(mockDelete) #4
.toHaveBeenCalledWith("Item B"); #4
});
The test combines vi.fn(), a user-event click, and toHaveBeenCalledWith to check the delete callback’s argument. section 10.2.2
The default test run watches for file changes; use vitest run for a single run. Vitest also supports Istanbul coverage after additional configuration, producing a terminal summary and an HTML report. Use line and branch coverage to spot gaps, not as a blanket demand for 100%. section 10.1.1 section 13.1.2
