---
title: "Спящий тест"
type: "test-smell"
slug: "sleepy-test"
url: "http://localhost:3000/ru/test-smells/sleepy-test.md"
category: "Запахи нестабильности"
description: "Спящий тест приостанавливает выполнение захардкоженной задержкой (`Thread.sleep`, `setTimeout`, `cy.wait(2000)`, `page.waitForTimeout`), чтобы дождаться асинхронной работы, вместо того чтобы ждать само нужное условие."
---
# Спящий тест

> Спящий тест приостанавливает выполнение захардкоженной задержкой (`Thread.sleep`, `setTimeout`, `cy.wait(2000)`, `page.waitForTimeout`), чтобы дождаться асинхронной работы, вместо того чтобы ждать само нужное условие.

## Signs and Symptoms

Вы видите задержку с «магическим числом», сидящую между действием и его проверкой, с комментарием, извиняющимся за неё:

```js
test('order is processed', async () => {
  submitOrder(order);

  // дать воркеру время завершиться
  await new Promise((r) => setTimeout(r, 2000));

  expect(await getOrderStatus(order.id)).toBe('processed');
});

```

Выдающие признаки:

* Фиксированные паузы в телах тестов или хуках: `Thread.sleep(500)`, `await sleep(1000)`, `time.sleep(2)`, `cy.wait(3000)`, `await page.waitForTimeout(2000)`, `browser.pause(2000)`.
* Комментарии вроде `// ждём анимацию`, `// дать БД догнать`, `// без этого нестабильно`.
* Длительность паузы со временем ползёт вверх (`1000` становится `2000`, становится `5000`), пока люди борются с эпизодическими падениями, наращивая задержку.
* Число — _единственная_ синхронизация: нет ни проверки, ни опроса, ни события, к которому это ожидание на самом деле привязано.
* Медленный набор, где реальное время прогона определяется паузами, а не реальной работой.

Запах — о _безусловном_ ожидании. `cy.wait('@apiAlias')` или `await expect(locator).toBeVisible()` ждут условие и это нормально; `cy.wait(2000)` и `page.waitForTimeout(2000)` ждут по часам и это не нормально.

## Reasons for the Problem

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

* У асинхронной операции (очередь, таймер, анимация, сетевой вызов, фоновый поток) нет очевидной зацепки, на которой можно подождать, поэтому задержка — путь наименьшего сопротивления.
* Тест время от времени падал, и кто-то «починил» его, вставив или удлинив паузу, пока он не позеленел на его машине.
* Тест взаимодействует с реальной внешней системой (часы, планировщик, файловая система, HTTP-сервис), завершение которой не наблюдаемо, поэтому время подбирается наугад.

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

* **Надёжность / ложная уверенность.** Пауза кодирует допущение — «работа завершится за N мс». Время обработки варьируется между машинами, нагрузкой CI и прогонами, поэтому тест недетерминирован: он проходит локально и падает на загруженном агенте CI или, хуже того, пауза _слишком коротка_ и проверка состязается с кодом, проходя лишь по счастливому стечению таймингов. Это одна из самых упоминаемых коренных причин нестабильных (flaky) тестов.
* **Медленность.** Фиксированная пауза всегда ждёт всю длительность, даже когда работа завершилась за 20 мс. Умноженные на весь набор, паузы превращают секунды реальной работы в минуты простоя, что отбивает желание запускать тесты часто.
* **Сопровождаемость.** «Магическое число» хрупко: ускорение или замедление проверяемой системы тихо ломает временной контракт, и единственная «починка», к которой прибегают, — увеличить число; этот храповик делает набор медленнее и _всё равно_ нестабильным.
* **Читаемость.** Задержка скрывает, _чего_ тест на самом деле ждёт. Читатель не может сказать, охраняет ли `sleep(2000)` запись в БД, отрисовку или вообще ничего, поэтому намерение теста затемнено.

## Treatment

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

1. **Опрашивайте условие.** Используйте помощник опроса/повтора — Awaitility (JVM), `vi.waitFor` / `waitFor` (Vitest, Testing Library), `waitFor` из Jest или авто-повторяющиеся проверки вашего раннера, — чтобы тест продолжился в тот же миг, как условие станет истинным, и падал лишь по истечении таймаута.
2. **Предпочитайте встроенные ожидающие проверки.** Веб-инструменты E2E уже повторяют: web-first проверки Playwright (`await expect(locator).toBeVisible()`) и авто-повторяющиеся команды Cypress вовсе устраняют нужду ждать.
3. **Ждите события, а не часы.** Дождитесь промиса, выполните `await` сетевого ответа или используйте `CountDownLatch`/колбэк/`waitFor('@alias')`, который продакшен-код действительно сигнализирует.
4. **Управляйте временем вместо того, чтобы его тратить.** Когда задержка — реальный таймер в проверяемом коде, используйте поддельные таймеры (`jest.useFakeTimers()`, `vi.useFakeTimers()`, `vi.advanceTimersByTimeAsync`), чтобы детерминированно продвигать часы, а не спать.

До:

```js
test('order is processed', async () => {
  submitOrder(order);
  await new Promise((r) => setTimeout(r, 2000)); // сонно
  expect(await getOrderStatus(order.id)).toBe('processed');
});

```

После (опрос условия с ограниченным таймаутом):

```js
import { vi } from 'vitest';

test('order is processed', async () => {
  submitOrder(order);
  await vi.waitFor(
    async () => expect(await getOrderStatus(order.id)).toBe('processed'),
    { timeout: 5000, interval: 50 },
  );
});

```

Эквиваленты для Playwright/Cypress:

```js
// Playwright — web-first проверка автоматически повторяется до видимости или таймаута
await expect(page.getByText('processed')).toBeVisible();

// Cypress — ждём запрос, а не число
cy.intercept('POST', '/orders').as('createOrder');
cy.wait('@createOrder');

```

Результат ждёт не дольше необходимого, быстро падает с ясным сообщением о таймауте и больше не зависит от скорости машины.

## Detected by

- **sonar** `java:S2925` — «Thread.sleep» не должен использоваться в тестах (https://rules.sonarsource.com/java/RSPEC-2925/)
- **eslint-plugin-playwright** `playwright/no-wait-for-timeout` — no-wait-for-timeout (запрет page.waitForTimeout) (https://github.com/playwright-community/eslint-plugin-playwright/blob/main/docs/rules/no-wait-for-timeout.md)
- **eslint-plugin-cypress** `cypress/no-unnecessary-waiting` — no-unnecessary-waiting (запрет cy.wait с числом) (https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/no-unnecessary-waiting.md)
- **eslint-plugin-ui-testing** `ui-testing/no-hard-wait` — no-hard-wait (cy.wait, page.waitForTimeout, browser.pause, t.wait) (https://github.com/kwoding/eslint-plugin-ui-testing/blob/master/docs/rules/no-hard-wait.md)
