Моки, стаби, фікстури, фабрики
Основна мета
Section titled “Основна мета”Моки, стаби, фікстури та фабрики допомагають ізолювати юніт, контролювати залежності та робити тести детермінованими.
Їхня спільна ціль - забезпечити передбачувані умови без звернення до реальних API, таймерів або зовнішніх систем.
| Тип | Призначення | Приклад |
|---|---|---|
| Mock | Підміна залежності або модуля | vi.mock('@/sdk/apiClient') |
| Stub | Підміна функції з контрольованим виходом | vi.fn().mockReturnValue(...) |
| Fixture | Готові тестові дані | userFixture, ordersFixture |
| Factory | Генератор динамічних даних | userFactory({ role: 'admin' }) |
Моки (Mocks)
Section titled “Моки (Mocks)”Моки модулів
Section titled “Моки модулів”Використовуються для підміни цілих модулів або залежностей, наприклад SDK, router, storage.
vi.mock('@/sdk/apiClient', () => ({ apiClient: { get: vi.fn().mockResolvedValue({ data: mockUser }), post: vi.fn(), },}));- Мок завжди повертає детермінований результат.
- Використовуємо
vi.mockтільки для зовнішніх залежностей (API, router, timers, storage). - Моки мають бути локальні до тесту --- не глобальні.
- Для повторного використання моки виносяться у
tests/mocks/.
Антипатерн:
❌ vi.mock('react-router-dom') - якщо можна протестувати через поведінку (навігацію), а не через мок.
Моки API через MSW
Section titled “Моки API через MSW”Для REST або GraphQL - перевага за MSW (Mock Service Worker).
Це дозволяє тестувати так, ніби відбувається реальний HTTP-запит.
import { http, HttpResponse } from 'msw';
export const handlers = [ http.get('/api/users', () => HttpResponse.json([{ id: 1, name: 'Alice' }]) ),];→ Підключається в setupTests.ts автоматично.
Переваги MSW:
- Зберігає реальну поведінку fetch/axios.
- Дозволяє тестувати UI без прямої залежності від API.
- Додає можливість симулювати помилки, таймаути, 404 тощо.
Стаби (Stubs)
Section titled “Стаби (Stubs)”const fetchStub = vi.fn().mockResolvedValue({ json: () => Promise.resolve({ id: 1 }),});- Використовується у простих кейсах без складного флоу.
- Добре підходить для тестів чистих функцій, де потрібно підмінити лише частину поведінки.
- Для перевірки викликів - додається
expect(fetchStub).toHaveBeenCalled().
Фікстури (Fixtures)
Section titled “Фікстури (Fixtures)”Directorytests
Directoryfixtures
- user.admin.json
- user.viewer.json
- orders.small.json
- orders.large.json
Правила:
- Мінімальні, але достатні для відтворення логіки.
- Назви повинні описувати суть:
user.admin.json,product.out-of-stock.json. - Не використовуємо продакшн-дампи.
- У TypeScript можна створити TS-фікстури:
export const userFixture = {id: 1,name: 'Alice',role: 'admin',};Фабрики (Factories)
Section titled “Фабрики (Factories)”export const userFactory = (overrides?: Partial<User>) => ({ id: Math.floor(Math.random() * 1000), name: 'John Doe', role: 'viewer', ...overrides,});Викоистання:
const admin = userFactory({ role: 'admin' });const viewer = userFactory({ role: 'viewer' });Комбінування фікстур і фабрик
Section titled “Комбінування фікстур і фабрик”- Фікстури — для статичних, канонічних даних.
- Фабрики — для швидкого створення варіантів у межах тесту.
const baseUser = userFixture;const randomUsers = Array.from({ length: 3 }, () => userFactory({ role: 'editor' }));Моки браузерних API
Section titled “Моки браузерних API”Підміняємо API, яких немає у jsdom, у setupTests.ts.
Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation(query => ({ matches: false, media: query, addListener: vi.fn(), removeListener: vi.fn(), })),});Інші приклади:
ResizeObserver, IntersectionObserver, navigator.clipboard, window.scrollTo.
Скидання моків
Section titled “Скидання моків”Перед кожним тестом:
beforeEach(() => { vi.clearAllMocks(); vi.resetModules();});Це запобігає “витіканню” стану моків між тестами.
Стандарти розміщення
Section titled “Стандарти розміщення”Directorytests
Directorymocks/ ← модулі/сервіси, які підмінюються часто
- …
Directoryfixtures/ ← статичні JSON або TS-об’єкти
- …
Directoryfactories/ ← функції для динамічних даних
- …
Directorymsw/ ← http/graphQL обробники
- …