Реальні компоненти рідко живуть самі по собі: вони читають тему, поточного користувача, маршрут, клієнт запитів. Без провайдерів у тесті вони падають або поводяться не так, як у застосунку.
Найпростіше - wrapper у render:
render(<Header />, {
wrapper: ({ children }) => <AuthContext value={{ user: testUser, logout: vi.fn() }}>{children}</AuthContext>,
});
Для всього проєкту - власна функція render, яка обгортає компонент усім потрібним і дає змогу налаштувати кожен провайдер:
// src/test/render.tsx
import { render, type RenderOptions } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
type Options = RenderOptions & { user?: User | null; route?: string };
export function renderWithProviders(ui: ReactElement, { user = null, route = '/', ...options }: Options = {}) {
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
function Providers({ children }: { children: ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<AuthContext value={{ user, logout: vi.fn() }}>
<MemoryRouter initialEntries={[route]}>{children}</MemoryRouter>
</AuthContext>
</QueryClientProvider>
);
}
return render(ui, { wrapper: Providers, ...options });
}
export * from '@testing-library/react';
import { renderWithProviders, screen } from '../test/render';
test('гість бачить кнопку входу', () => {
renderWithProviders(<Header />, { user: null });
expect(screen.getByRole('link', { name: 'Увійти' })).toBeInTheDocument();
});
Що варто врахувати:
- свіжий стан на кожен тест: клієнт запитів, сховище (Zustand, Redux) створюються всередині функції, а не на рівні модуля - інакше кеш з одного тесту впливає на інший;
retry: falseдля TanStack Query - інакше тест помилки чекатиме повторних спроб;- маршрутизатор у пам'яті (
MemoryRouter,createMemoryRouter) - без реального URL браузера; початковий маршрут задається параметром; - підміняти лише те, що поза межами тесту: справжній контекст з тестовими даними краще за мок
useContext- тест перевіряє реальну інтеграцію; - перевірка поведінки при різному контексті (гість / адміністратор / інша мова) стає одним параметром.
renderHook приймає той самий wrapper - хуки тестуються з тими самими провайдерами.
Докладніше в документації: React Testing Library: власний render