Modern QA2026Утверждения и нестабильные тесты
Join

Course26 Testing Like a Senior

Foundations · Chapter 26

Утверждения и нестабильные тесты

Updated Jul 2026

Две проблемы определяют ранний опыт автоматизации: утверждения, которые проходят, когда должны падать (или падают, не объясняя почему), и тесты, которые то проходят, то падают без видимой причины. У обеих проблем общая корневая причина — тесты, спроектированные вокруг happy path, а не для диагностической ясности и детерминированного выполнения.

Утверждения, которые тратят время впустую

Антипаттерн: Общие утверждения вроде expect(result).toBe(true) или expect(status).toBe(200). При сбое сообщение об ошибке — «expected true, received false» — не говорит ничего о том, что пошло не так.

Паттерн: Диагностические утверждения, которые сообщают почему они упали, а не только что упали.

Пример антипаттерна:

// Падает с: "Expected true, received false"
expect(response.success).toBe(true);

Пример паттерна:

// Падает с: "Expected status 200, received 503. Body: { error: 'Database connection timeout' }"
expect(response.status, `Body: ${JSON.stringify(response.body)}`).toBe(200);

Мягкие утверждения (soft assertions) — Собирают несколько сбоев за один прогон теста вместо остановки на первом. Полезны для валидации форм, где вы хотите знать обо всех полях, которые не прошли проверку, а не только о первом.

Кастомные хелперы утверждений — Доменно-специфичные утверждения вроде expectValidOrder(order), которые проверяют несколько условий и выдают читаемые сообщения об ошибках. Они встраивают знания вашей команды о качестве в переиспользуемый код.

Нестабильные тесты и детерминированный дизайн

Нестабильный тест — это тест, который проходит и падает без какого-либо изменения кода. Нестабильные тесты разрушают доверие к набору тестов — когда сбой может быть «просто нестабильностью», разработчики перестают расследовать сбои.

Корневые причины нестабильности

Источник Пример Исправление
Тайминг / состояние гонки Утверждение до завершения асинхронной операции Используйте событийные ожидания, а не sleep()
Общее состояние Тест A создаёт данные, от которых зависит тест B Изолируйте тестовые данные для каждого теста
Внешние зависимости Тест вызывает живое стороннее API, которое работает медленно или недоступно Мокайте внешние сервисы
Зависимость от порядка Тесты проходят последовательно, но падают при перемешивании Каждый тест должен настраивать собственные предусловия
Недетерминированные данные Утверждение на временных метках или случайных ID Утверждайте на стабильных свойствах, используйте паттерны/диапазоны

Антипаттерн: Добавить повторные запуски (retries), чтобы нестабильные тесты проходили. У теста по-прежнему баг — вы просто спрятали его за повторными запусками.

Паттерн: Проектируйте тесты для детерминизма с самого начала. При появлении нестабильности расследуйте и исправляйте корневую причину. Отслеживайте процент нестабильных тестов на дашборде — растущий процент нестабильности является ранним сигналом того, что тестовой архитектуре требуется внимание.

Ключевые выводы

  • Пишите утверждения, которые диагностируют сбои, а не просто обнаруживают их — включайте контекст в сообщения об ошибках
  • Используйте мягкие утверждения, когда нужно проверить несколько условий в одном тесте
  • Создавайте кастомные хелперы утверждений, кодирующие доменные знания
  • У нестабильных тестов есть корневые причины: тайминг, общее состояние, внешние зависимости, зависимость от порядка, недетерминированные данные
  • Никогда не используйте sleep() как стратегию синхронизации — используйте событийные ожидания
  • Отслеживайте процент нестабильных тестов и рассматривайте растущую нестабильность как проблему инфраструктуры, а не отдельных тестов