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. Це класичний конфлікт спільного стану.

Рішення

  1. Унікальні дані на тест. Фікстура створює нового користувача і рахунок перед тестом.
  2. Пул акаунтів на воркер, якщо створення дороге. Воркер з індексом N бере акаунти з пулу N. Worker-scoped фікстура в Playwright створює їх один раз на воркер.
  3. Префікс прогону. Усі сутності мають мітку прогону, наприклад run_8f2c_w3_. Так легко знайти і прибрати сміття після прогону.
  4. Перевірки відносно, а не абсолютно, якщо стан спільний: запам'ятати баланс до дії і порівняти різницю.
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 використовую в окремому невеликому наборі.