Skip to content

Моки, стаби, фікстури, фабрики

Моки, стаби, фікстури та фабрики допомагають ізолювати юніт, контролювати залежності та робити тести детермінованими.
Їхня спільна ціль - забезпечити передбачувані умови без звернення до реальних API, таймерів або зовнішніх систем.

ТипПризначенняПриклад
MockПідміна залежності або модуляvi.mock('@/sdk/apiClient')
StubПідміна функції з контрольованим виходомvi.fn().mockReturnValue(...)
FixtureГотові тестові даніuserFixture, ordersFixture
FactoryГенератор динамічних данихuserFactory({ role: 'admin' })

Використовуються для підміни цілих модулів або залежностей, наприклад 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') - якщо можна протестувати через поведінку (навігацію), а не через мок.

Для REST або GraphQL - перевага за MSW (Mock Service Worker).
Це дозволяє тестувати так, ніби відбувається реальний HTTP-запит.

tests/msw/handlers.ts
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 тощо.
const fetchStub = vi.fn().mockResolvedValue({
json: () => Promise.resolve({ id: 1 }),
});
  • Використовується у простих кейсах без складного флоу.
  • Добре підходить для тестів чистих функцій, де потрібно підмінити лише частину поведінки.
  • Для перевірки викликів - додається expect(fetchStub).toHaveBeenCalled().
  • 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',
};
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, яких немає у 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.

Перед кожним тестом:

beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
});

Це запобігає “витіканню” стану моків між тестами.

  • Directorytests
    • Directorymocks/ ← модулі/сервіси, які підмінюються часто
    • Directoryfixtures/ ← статичні JSON або TS-об’єкти
    • Directoryfactories/ ← функції для динамічних даних
    • Directorymsw/ ← http/graphQL обробники