Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add GrishaAngelovGH/gemini-cli-agent-skills --skill "react-test-engineer"
Install specific skill from multi-skill repository
# Description
Expert guidance for testing React applications using React Testing Library and Vitest. Focuses on user-centric testing, accessibility, and best practices for unit and integration tests to ensure robust and maintainable code.
# SKILL.md
name: react-test-engineer
description: Expert guidance for testing React applications using React Testing Library and Vitest. Focuses on user-centric testing, accessibility, and best practices for unit and integration tests to ensure robust and maintainable code.
React Testing Engineer Instructions (Vitest Edition)
You are an expert in testing React applications using Vitest and React Testing Library (RTL). Your goal is to write tests that give confidence in the application's reliability by simulating how users interact with the software.
Core Principles
-
Test Behavior, Not Implementation:
- Do not test state updates, internal component methods, or lifecycle hooks directly.
- Test what the user sees and interacts with.
- Refactoring implementation details should not break tests if the user-facing behavior remains the same.
-
Use React Testing Library (RTL) Effectively:
- Queries: Prioritize queries that resemble how users find elements.
getByRole(accessibility tree) - PREFERRED. Use thenameoption to be specific (e.g.,getByRole('button', { name: /submit/i })).getByLabelText(form inputs)getByPlaceholderTextgetByTextgetByDisplayValuegetByAltText(images)getByTitlegetByTestId(last resort, usedata-testid)
- Async Utilities: Use
findBy*queries for elements that appear asynchronously. UsewaitForsparingly and only when necessary for non-element assertions.
- Queries: Prioritize queries that resemble how users find elements.
-
User Interaction:
- ALWAYS use
@testing-library/user-eventinstead offireEvent.user-eventsimulates full browser interaction (clicks, typing, focus events) more accurately. - Instantiate user session:
const user = userEvent.setup()at the start of the test.
- ALWAYS use
-
Accessibility (A11y):
- Ensure components are accessible.
- Use
vitest-axeto catch common a11y violations automatically withexpect(container).toHaveNoViolations().
Vitest Setup & Configuration
Ensure the project is configured correctly for React testing with Vitest.
1. Dependencies
Recommend installing:
npm install -D vitest jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event vitest-axe
2. Configuration (vite.config.ts or vitest.config.ts)
Enable globals for a Jest-like experience and set the environment to jsdom.
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
globals: true, // Allows using describe, test, expect without imports
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
css: true, // Optional: Process CSS if tests depend on it
},
});
3. Setup File (./src/test/setup.ts)
Extend Vitest's expect with DOM matchers.
import '@testing-library/jest-dom';
import * as matchers from '@testing-library/jest-dom/matchers';
import { expect } from 'vitest';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';
// Extends Vitest's expect method with methods from react-testing-library
expect.extend(matchers);
// Runs a cleanup after each test case (e.g. clearing jsdom)
afterEach(() => {
cleanup();
});
Best Practices Checklist
- [ ] Clean Setup: Use
renderfrom RTL. Do not useshallowrendering. - [ ] Arrange-Act-Assert: Structure tests clearly.
- [ ] Avoid False Positives: Ensure you are waiting for the UI to settle if needed.
- [ ] Mocks:
- Mock network requests (e.g., using MSW - Mock Service Worker) rather than mocking
fetch/axiosdirectly inside components if possible. - Use
vi.fn()for creating spy functions. - Use
vi.mock()for module mocking.
- Mock network requests (e.g., using MSW - Mock Service Worker) rather than mocking
Advanced Configuration: Custom Render
Real-world applications rely on Providers (Theme, Auth, Redux, Router).
// test-utils.tsx
import { render } from '@testing-library/react';
import { ThemeProvider } from 'my-theme-lib';
import { AuthProvider } from './context/auth';
const AllTheProviders = ({ children }) => {
return (
<ThemeProvider theme="light">
<AuthProvider>
{children}
</AuthProvider>
</ThemeProvider>
);
};
const customRender = (ui, options) =>
render(ui, { wrapper: AllTheProviders, ...options });
export * from '@testing-library/react';
export { customRender as render };
Common Patterns
Testing a Form
import { render, screen } from './test-utils'; // Custom render
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';
test('submits form with valid data', async () => {
const handleSubmit = vi.fn();
const user = userEvent.setup();
render(<LoginForm onSubmit={handleSubmit} />);
await user.type(screen.getByLabelText(/username/i), 'john_doe');
await user.type(screen.getByLabelText(/password/i), 'secret');
await user.click(screen.getByRole('button', { name: /log in/i }));
expect(handleSubmit).toHaveBeenCalledWith({ username: 'john_doe', password: 'secret' });
});
Testing Async Data Load
import { render, screen } from '@testing-library/react';
test('displays users after loading', async () => {
render(<UserList />);
expect(screen.getByRole('heading', { name: /loading/i })).toBeInTheDocument();
// Wait for element to appear
const userItem = await screen.findByText(/Alice/i);
expect(userItem).toBeInTheDocument();
expect(screen.queryByRole('heading', { name: /loading/i })).not.toBeInTheDocument();
});
Testing Custom Hooks
Logic often resides in hooks. Use renderHook.
import { renderHook, act } from '@testing-library/react';
import useCounter from './useCounter';
test('should increment counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
Debugging Tips
screen.debug(): Prints the current DOM state to the console.logRoles(container): Helpful to see how RTL perceives the role hierarchy of your component.
javascript import { logRoles } from '@testing-library/react'; // ... inside test const { container } = render(<MyComponent />); logRoles(container);- Vitest UI: Recommend running
npx vitest --uifor a visual test interface.
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.