Независимость теста - это не пожелание, а условие, при котором E2E-набор вообще можно поддерживать. Playwright строит её на изоляции: каждый тест получает свой BrowserContext. Контекст похож на быстрый инкогнито-профиль - у него собственные cookies, localStorage и sessionStorage, не пересекающиеся с другими тестами. Из этого следует жёсткое требование: тест обязан проходить сам по себе, в любом порядке и параллельно с остальными.
BrowserContext дешевле, чем кажется. Он не запускает новый браузер целиком, а создаёт изолированное пространство состояния внутри уже поднятого движка, поэтому создание контекста на каждый тест почти бесплатно. Именно это делает изоляцию практичной: не нужно выбирать между чистотой и скоростью - Playwright даёт свежий чистый профиль каждому тесту без заметных накладных расходов, и это фундамент всей параллельности.
Прямое следствие изоляции - запрет на цепочки тестов. Соблазн написать три сценария подряд - "создаёт заказ", "оплачивает созданный заказ", "удаляет этот заказ" - понятен: кажется, что второй просто использует результат первого. Но при изоляции второй тест не видит состояния первого, а при параллельном прогоне они вообще идут одновременно. Такая цепочка либо не работает, либо работает случайно, пока порядок совпадает.
Правильная форма - самостоятельные сценарии, каждый со своими данными. Тест "оплачивает заказ" не полагается на предыдущий, а получает собственный заказ - через fixture, который создаёт его под этот тест. Тогда сценарий проходит в любом порядке и на любом worker, потому что не зависит ни от чьих следов. Данные приходят в тест как его собственный вход, а не как побочный эффект соседнего сценария.
Полезно один раз увидеть антипаттерн и правильную форму рядом, чтобы отличать их при ревью. Ниже - цепочка зависимых тестов против самостоятельного сценария, получающего собственный заказ через fixture. К этому сравнению возвращаются, когда возникает искушение "сэкономить" и переиспользовать в одном тесте то, что создал другой, - экономия оборачивается недетерминизмом.
Есть законное исключение - последовательный режим. test.describe.serial выполняет тесты по порядку и останавливает остальные при падении одного. Но это узкий инструмент для действительно неделимого процесса, который нельзя разбить на самостоятельные шаги, - например, многоэтапный мастер, где каждый шаг физически продолжает предыдущий в одной сессии. Serial оправдан только тогда, а не как удобный способ переиспользовать состояние.
Опасность serial в том, что он маскирует архитектурную проблему. Переведя связанные тесты в последовательный режим ради того, чтобы второй видел данные первого, вы прячете отсутствие изоляции и заодно блокируете параллельность: серия идёт в одном worker и не делится. То, что выглядело как удобство, становится тормозом набора и точкой хрупкости - падение первого шага рушит всю серию.
Типичные провалы изоляции сводятся к попыткам её обойти. Цепочка тестов, где следующий рассчитывает на состояние предыдущего, - зелёная по порядку и красная при параллельности или перестановке. Serial-режим как способ переиспользовать данные вместо создания собственных - спрятанная архитектурная проблема и потерянная параллельность. Давайте каждому тесту свои данные и свой контекст, а serial берегите для действительно неделимых процессов.
// Антипаттерн: цепочка, где тест рассчитывает на состояние предыдущего
test('создаёт заказ', ...)
test('оплачивает созданный заказ', ...)
test('удаляет этот заказ', ...)
// Правильно: самостоятельный сценарий получает собственный заказ через fixture
test('оплачивает заказ', async ({ page, order }) => {
// fixture создал заказ именно под этот тест
})