Verify that your UI works, automated.
Please read:
- Testing Libray:
- @testing-library/react
- @testing-library/user-event
- @testing-library/jest-dom
- Vitest
- How many of you have written at least 5 unit tests (in whatever language) before?
- How many of you have mocked a dependency while unit testing?
- Focus on testing from user point of view
- Good support for modules, TypeScript, JSX
- Fast and good debugging support
- Widely used / fast adoption, active and friendly community
- jsdom to simulate browser in Node.js
- jest-dom with additional matchers
- setup file
- cleanup
- describe
- it
- expect, matchers
- run / watch
- coverage
- before / after
- mocking modules
- mocking functions, like fetch
- setup
- render
- screen
- queries
- click
- type
Testing app from first section of this course: Getting Started
- Install deps
npm install vitest jsdom
npm install @testing-library/react @testing-library/user-event @testing-library/jest-dom
- Configure Vitest in
vite.config.ts
:
/// <reference types="vitest" />
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
setupFiles: ["vitest.setup.ts"],
},
});
- Configure Testing Library and jest-dom in
vitest.setup.ts
:
import '@testing-library/jest-dom/vitest';
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";
afterEach(cleanup);
- Configure jest-dom in
tsconfig.json
:
"include": ["src", "./vitest.setup.ts"],
- Write test for initial App render in
App.test.tsx
:
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import App from "./App";
describe(App.name, () => {
it("should render", () => {
render(<App />);
expect(screen.getByLabelText("Artist name:")).toBeInTheDocument();
});
});
- Run Vitest
npx vitest
- View coverage
npx vitest --run --coverage
# then open coverage/index.html
-
mock-response.json - save response of https://musicbrainz.org/ws/2/release?fmt=json&query=artist:rihanna
-
Fix AlbumPicker component to work with jsdom
const form = e.target as HTMLFormElement;
const formElements = form.elements as typeof form.elements & {
artist: { value: string };
};
const artist = encodeURIComponent(formElements.artist.value);
<form onSubmit={handleSubmit} aria-label="search">
- Write test for AlbumPicker in
App.test.tsx
:
describe(AlbumPicker.name, () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("should show search results", async () => {
const user = userEvent.setup();
const mockFetch = vi.spyOn(window, "fetch").mockImplementation(async () => {
return {
json: async () => mockResponse,
} as Response;
});
render(<AlbumPicker />);
const artistInput = screen.getByLabelText("Artist name:");
await user.type(artistInput, "rihanna");
const form = screen.getByRole("form", { name: "search" });
fireEvent.submit(form);
await screen.findByText("A Girl Like Me");
expect(mockFetch).toHaveBeenCalledWith(
"https://musicbrainz.org/ws/2/release?fmt=json&query=artist:rihanna"
);
});
});
View the page in a browser at specific points in your test with vitest-preview.
- Install and run vitest-preview:
npm install vitest-preview
npx vitest-preview
- Add to test to display in vitest-preview:
import { debug } from "vitest-preview";
// ....
await screen.findByText("A Girl Like Me");
debug();
Run the test and see the result in the browser window opened by vitest-preview:
npx vitest --run
Run tests on every push.
Add line inside "scripts" in package.json
:
"test": "tsc && vitest --run --coverage && vite build"
If your repo is on GitHub, create this .github/workflows/ci.yml
file:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: "18"
- run: npm ci
- run: npm test
Push to GitHub and see the result of the test.