automationflaky-tests

Параллелизация автотестов: 5 ловушек, которые превращают её в ад

Параллелить тесты — очевидное решение для ускорения CI. Был один воркер по 60 минут, сделали 10 — получили 6 минут. На бумаге. На практике без подготовки получаешь flaky-тесты, гонки за данные и неприятные сюрпризы.

Ловушка 1: Shared test data

Тест A создаёт юзера [email protected]. Тест B логинится тем же [email protected]. В параллели:

— А только начал создавать, B пытается логиниться → 404. — А успел создать, B логинится — OK, удаляет данные. А продолжает работу → его юзер уже удалён.

Решение: каждый тест создаёт свой уникальный test data. Не используй фиксированные email/имя — генерируй уникальный по namespace:

const email = `test-${testInfo.workerIndex}-${Date.now()}@example.com`;

Ловушка 2: Shared external state

Тесты пишут в один и тот же файл, БД, S3-бакет, очередь. В параллели — race condition.

Решение: namespace по worker:

  • Файлы: tests/output/worker-${id}/.
  • БД: каждому worker’у своя схема (PostgreSQL schemas) или своя БД.
  • S3: prefix=test-runs/${runId}/${workerId}/.

Ловушка 3: Браузер с одним user state

Если тест использует shared browser context (один Chrome для всех тестов), куки/localStorage пересекаются. Юзер из теста А логинится — тест B неожиданно «уже залогинен».

Решение: каждый тест получает свой context. В Playwright это by-default. В Selenium WebDriver нужно явно вызывать quit() и стартовать новый.

Ловушка 4: Order dependency

«Сначала тест на создание, потом на просмотр». Поставили параллельно — порядок не гарантирован — fail.

Решение: тесты должны быть полностью независимыми. Если для теста «просмотр» нужен созданный юзер — создай его в beforeEach, а не в отдельном тесте.

Антипаттерн:

test('1. create user', async () => { ... });
test('2. login as user', async () => { ... }); // depends on test 1

Правильно:

test('login flow', async () => {
  await createUser();
  await login();
});

Ловушка 5: Резкое увеличение нагрузки на тестовое окружение

10 параллельных тестов = 10× запросов к API = на staging-сервере 100% CPU = 500-ошибки = тесты ложно красные.

Решение: — Профилируй staging — выдержит ли load? — Используй rate limiting в тестах (если делаешь много запросов). — Подними отдельный test environment для CI (контейнер на каждый PR). — Используй моки для third-party API (WireMock, MSW).

Бонус: параллелизм != скорость

Если тесты делают что-то одноразовое (например, один общий setup на 60 секунд), параллелизация даёт меньше выгоды.

Метрика: запусти 1 тест → замерь время → запусти 10 параллельно → должно быть близко к одному. Если 10 тестов идут 3× медленнее одного — bottleneck не CPU/network, а где-то в shared resource.

Чек-лист готовности к параллели

✅ Каждый тест работает в одиночку. ✅ Каждый тест работает в порядке: test1 → test2 → test1. ✅ Каждый тест работает после npx playwright test --shuffle. ✅ Тестовое окружение выдерживает 10× нагрузку. ✅ Нет shared file/DB/S3 без namespacing.

Если все 5 — да, параллелизация даст реальную выгоду. Если хоть одно нет — сначала чини изоляцию.

Подробнее: Playwright Parallelism, Martin Fowler — Eradicating Non-Determinism.