---
title: "Зависимость от порядка тестов"
type: "test-smell"
slug: "test-order-dependency"
url: "http://localhost:3000/ru/test-smells/test-order-dependency.md"
category: "Запахи нестабильности"
description: "Тест проходит или падает в зависимости от того, какие другие тесты выполнялись до него, потому что тесты «протекают» и полагаются на общее изменяемое состояние вместо того, чтобы каждый сам создавал и убирал свою фикстуру."
---
# Зависимость от порядка тестов

> Тест проходит или падает в зависимости от того, какие другие тесты выполнялись до него, потому что тесты «протекают» и полагаются на общее изменяемое состояние вместо того, чтобы каждый сам создавал и убирал свою фикстуру.

## Signs and Symptoms

Результат теста зависит от **порядка**, в котором выполняется набор, а не только от тестируемого кода. Предполагается, что каждый тест самодостаточен, но здесь один тест незаметно полагается на побочные эффекты, оставленные другим.

Характерные признаки:

* Тест **проходит в полном наборе, но падает при запуске в одиночку** (через `.only`, фильтр по имени или запуск только этого файла). Месарош называет это _одиноким тестом_ (Lonely Test).
* Тесты **ломаются, когда вы перемешиваете, шардируете или распараллеливаете** запуск, или после обновления раннера, меняющего порядок по умолчанию.
* **Удаление, пропуск или перестановка** одного теста приводит к падению, казалось бы, не связанного с ним теста.
* Одно падение запускает **каскад** последующих падений (_взаимодействующие тесты_ (Interacting Tests) у Месароша; _война тестовых прогонов_ (Test Run War) у ван Дёрсена, когда параллельные раннеры сталкиваются из-за общей фикстуры).
* Тесты **периодически нестабильны (flaky)** без изменения кода — зависимость от порядка одна из самых частых первопричин нестабильных тестов.

Структурный признак — **общее изменяемое состояние**, которое читается между тестами: переменные уровня модуля/`static`/глобальные, `beforeAll`, который однократно заполняет состояние, или несброшенный внешний ресурс (строки БД, файлы, кэши, переменные окружения, поддельные таймеры, моки).

```js
let users = []; // общее состояние уровня модуля

test('creates a user', () => {
  users.push({ id: 1, name: 'Ada' });
  expect(users).toHaveLength(1);
});

// Зелёный только потому, что тест выше выполнился первым и изменил `users`.
// Запустите этот тест в одиночку или перемешайте порядок — и он упадёт.
test('finds the created user', () => {
  expect(users.find(u => u.name === 'Ada')).toBeDefined();
});

```

## Reasons for the Problem

**Почему это происходит**

* **Общая фикстура** (созданная один раз в `beforeAll`, переменная уровня модуля, `static`\-поле класса или реальная база данных/файл) изменяется тестами и никогда не сбрасывается между ними, поэтому каждый тест наследует остатки от предыдущего.
* Удобство и скорость: разработчики переиспользуют дорогую подготовку или «достраивают» данные предыдущего теста, чтобы не создавать их заново (иногда это формализуется как _цепочки тестов_ (Chained Tests)).
* Зависимость **случайна и невидима** — ничто в коде теста не объявляет «запусти меня после того теста», поэтому она сохраняется до тех пор, пока не изменится порядок.

**Почему это вредно**

* **Надёжность / ложная уверенность.** Набор тестов зелёный по счастливой случайности порядка. Измените порядок, распараллельте или запустите подмножество — и тесты упадут (или, что хуже, реальная регрессия скрывается, потому что более ранний тест случайно оставил «правильное» состояние). Тесты, зависящие от порядка, — одна из главных причин _нестабильных (flaky) тестов_.
* **Сопровождаемость.** Вы не можете запустить, отладить или перезапустить один упавший тест в изоляции — сначала должен выполниться тест-предпосылка. Добавление, удаление или перестановка тестов вызывает «жуткое действие на расстоянии».
* **Читаемость.** Тест больше не документирует одно поведение с явными входными данными; чтобы понять его, нужно прочитать всё, что выполнялось до него (_таинственный гость_ (Mystery Guest), скрытый в порядке выполнения).
* **Блокирует параллелизм и выборочный запуск.** Шардирование тестов, параллельные раннеры и инструменты выбора/влияния тестов — все они предполагают независимость; связанность по порядку делает их небезопасными.

## Treatment

Сделайте каждый тест **независимым**: он сам настраивает всё необходимое, проверяет и убирает за собой, так что даёт одинаковый результат в любом порядке, в одиночку или в наборе.

1. **Дайте каждому тесту свежую фикстуру (Fresh Fixture).** Перенесите общую подготовку из `beforeAll` в `beforeEach` (или создавайте её внутри теста), чтобы состояние пересоздавалось для каждого теста, а не накапливалось.
2. **Устраните общее изменяемое состояние.** Не читайте и не пишите переменные уровня модуля, `static`\-поля или глобальные переменные между тестами. Создавайте объекты локально; передавайте данные явно.
3. **Сбрасывайте внешние ресурсы в teardown.** Откатывайте БД (транзакция на тест) или используйте уникальные данные для каждого теста; удаляйте временные файлы; восстанавливайте переменные окружения, глобальные значения и поддельные таймеры; очищайте моки (`jest.clearAllMocks()` / `vi.restoreAllMocks()`, `jest.resetModules()`). Предпочитайте автоматический/гарантированный teardown ручной очистке.
4. **Докажите независимость, рандомизируя порядок.** Это и есть настоящий детектор — это динамическое свойство, а не то, что может увидеть линтер:
  * Jest: `--randomize` / `randomize: true`.
  * Vitest: `sequence.shuffle` (конфиг или `--sequence.shuffle`).
  * pytest: `pytest-randomly` или `pytest-random-order`.
  * Maven Surefire: `-Dsurefire.runOrder=random`. Запускайте также подмножество/одиночный тест в CI, чтобы выявить _одинокие тесты_ (Lonely Tests).
5. **Если общая подготовка действительно нужна** (дорогая фикстура только для чтения), сделайте её **неизменяемой** и общей только для чтения, либо в крайнем случае используйте намеренно задокументированный набор _цепочечных тестов_ (Chained Test) — но никогда не случайную зависимость. Обратите внимание, что правило `no-hooks` из `eslint-plugin-jest`/`eslint-plugin-vitest` может отговорить от использования хуков setup/teardown, которые склонны провоцировать общее состояние, но само по себе оно не обнаруживает зависимость от порядка.

```js
// До — зависит от порядка: тесту 2 нужны остатки от теста 1
let users = [];
test('creates a user', () => {
  users.push({ id: 1, name: 'Ada' });
  expect(users).toHaveLength(1);
});
test('finds the created user', () => {
  expect(users.find(u => u.name === 'Ada')).toBeDefined();
});

// После — каждый тест владеет своей фикстурой
function makeRepo(seed = []) {
  return { users: [...seed] };
}

test('creates a user', () => {
  const repo = makeRepo();
  repo.users.push({ id: 1, name: 'Ada' });
  expect(repo.users).toHaveLength(1);
});

test('finds an existing user', () => {
  const repo = makeRepo([{ id: 1, name: 'Ada' }]); // задаёт своё собственное предусловие
  expect(repo.users.find(u => u.name === 'Ada')).toBeDefined();
});

```
