Параллелизация автотестов: 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.