QA Interview Hub · База знань · Тести
System Design
Тестові дані й оточення
Стратегії тестових даних
Тестові дані - найчастіша причина нестабільних і повільних тестів. Є кілька підходів, і Senior має пояснити, коли який.
| Підхід | Плюси | Мінуси |
|---|
| Статичні дані в БД | Просто, швидко | Конфлікти при паралельних прогонах, дані "псуються" |
| Генерація через API в тесті | Ізоляція, реалістично | Повільніше, залежність від API |
| Seed-скрипт або snapshot БД | Швидкий старт середовища | Треба підтримувати разом зі схемою |
| Фабрики плюс прямий запис у БД | Дуже швидко | Обходить бізнес-логіку, можна створити неможливий стан |
Базове правило: кожен тест створює свої дані і не покладається на дані інших тестів. Спільними можуть бути тільки довідники, які ніхто не змінює: валюти, країни, тарифи.
Для фінтеху важливий нюанс: створення "готового" користувача часто вимагає KYC, верифікації телефону, відкриття рахунку. Тут допомагають тестові хуки: внутрішній ендпоінт, доступний тільки в тестових середовищах, який створює верифікованого користувача одним запитом. Це треба узгодити з розробкою і закрити від продакшену.
Дані мають бути унікальними: email з testInfo.workerIndex і випадковим суфіксом, унікальні номери рахунків. Ніяких реальних персональних даних і реальних карток: тільки тестові картки провайдера.
На співбесіді: Мій принцип - кожен тест сам створює потрібні дані через API або тестові хуки і не залежить від інших тестів. Спільними залишаю тільки незмінні довідники, а реальні персональні дані в тестах не використовую взагалі.
Задача: тестові дані для паралельних прогонів
Умова: 64 паралельні воркери, спільний staging, тести переказів між рахунками. Тести періодично падають, бо баланс не той, що очікувався.
Чому падає
Два тести використовують одного користувача "test_user_1". Тест A переказує 30, тест B у цей момент перевіряє баланс 100 і отримує 70. Це класичний конфлікт спільного стану.
Рішення
- Унікальні дані на тест. Фікстура створює нового користувача і рахунок перед тестом.
- Пул акаунтів на воркер, якщо створення дороге. Воркер з індексом N бере акаунти з пулу N. Worker-scoped фікстура в Playwright створює їх один раз на воркер.
- Префікс прогону. Усі сутності мають мітку прогону, наприклад
run_8f2c_w3_. Так легко знайти і прибрати сміття після прогону. - Перевірки відносно, а не абсолютно, якщо стан спільний: запам'ятати баланс до дії і порівняти різницю.
export const test = base.extend<{}, { account: Account }>({
account: [async ({}, use, workerInfo) => {
const acc = await createAccount(`run_${RUN_ID}_w${workerInfo.workerIndex}`);
await use(acc);
await closeAccount(acc.id);
}, { scope: 'worker' }],
});
Trade-offs
Нові дані на кожен тест дають максимальну ізоляцію, але навантажують API і росте база. Пул на воркер швидший, але тести в межах воркера мають прибирати за собою стан.
Метрики
Кількість падінь через дані, обсяг сміття в БД після прогону, час на підготовку даних у тесті.
На співбесіді: Я прибираю спільний стан: кожен тест або кожен воркер отримує власні акаунти з міткою прогону. Якщо створення дороге, використовую worker-scoped фікстуру, а після прогону чищу дані за префіксом.
Оточення: локальне, ephemeral, staging
Тест-система - це не лише код тестів, а й середовища, де вони запускаються.
Локально (docker compose) -> швидкий цикл розробки тесту
Ephemeral env на PR -> ізольована перевірка зміни
Shared staging -> інтеграція з реальними залежностями
Production (smoke, synthetic) -> перевірка після релізу
Ephemeral environment - це середовище, яке піднімається на кожен PR (наприклад, namespace у Kubernetes) і знищується після merge. Плюс: немає конфліктів між командами, дані чисті. Мінус: вартість і час підняття, складно підняти всі 40 мікросервісів.
Компроміс: піднімати тільки змінений сервіс і його найближчі залежності, а решту брати зі спільного staging або замінити заглушками.
Зовнішні залежності. Платіжний провайдер, KYC-сервіс, банківські API часто мають sandbox з обмеженнями: rate limits, нестабільність, неможливість відтворити певні помилки. Тому для більшості тестів я ставлю заглушку на рівні HTTP (WireMock або аналог) з контрольованими відповідями: успіх, відмова, таймаут, дубль callback. Реальний sandbox залишаю для невеликого набору інтеграційних тестів, які йдуть окремо і не блокують PR.
На співбесіді: Я розділяю середовища за призначенням: локальне для розробки, ephemeral на PR для ізоляції, staging для інтеграції і smoke у проді. Зовнішні платіжні сервіси я в основному замінюю керованими заглушками, щоб відтворювати помилки і таймаути, а реальний sandbox використовую в окремому невеликому наборі.