Pull to refresh
4K+
-3
Дарья@Ariless

QA Automation Engineer

-9,4
Rating
4
Subscribers
Send message

TXORDER-01: 7 тестов прошли, 8-й нашёл баг

Как domain state в одном тесте сделал видимым баг в порядке операций внутри транзакции — и что это говорит о том, что на самом деле проверяют “зелёные тесты”

7 тестов прошли.

8-й нашёл баг в production flow.

Не потому что был написан лучше. Потому что запустился с другим начальным состоянием системы.

Операция и транзакция

PATCH /reschedule — перенос appointment пациента на другой слот. Атомарная транзакция: освободить старый слот, занять новый, переместить запись. Плюс promoteFromWaitlist: если на освобождённом слоте есть очередь, первый из неё автоматически получает appointment.

Порядок операций в транзакции:

  1. free_old_slot(slot1)

  2. promoteFromWaitlist(slot1)

  3. book_new_slot(slot2)

  4. move_appointment(appointment → slot2)

Почему 7 тестов ничего не нашли

Тесты 1–7 проверяли стандартные сценарии: перенести pending, перенести confirmed, попытаться перенести на занятый слот. Ни в одном из них не было пациента в вейтлисте.promoteFromWaitlist в каждом тесте — no-op. Очередь пуста, функция вызывалась, ничего не делала, возвращала успех. Это важная деталь: функция не падала. Она просто не активировалась. Порядок операций вокруг неё не имел значения — потому что одна из операций ничего не делала.

7 зелёных тестов говорили: reschedule работает корректно. На самом деле они говорили: reschedule работает корректно когда вейтлист пуст.

Что нашёл 8-й тест

Пациент 2 встал в очередь на slot1. Пациент 1 запустил reschedule на slot2.

Ответ: 409 SLOT_IN_USE.

Слот был свободен. Пациент имел право переноса. Транзакция откатилась.

Механизм

  1. free_old_slot(slot1) ← слот доступен

  2. promoteFromWaitlist(slot1) ← пациент 2 получил pending на slot1

  3. book_new_slot(slot2)

  4. move_appointment → slot2 ← appointment пациента 1 ещё на slot1

После шага 2 на slot1 два active appointment одновременно: пациента 1 (ещё не переехал) и пациента 2 (только что из промоушна). UNIQUE constraint one_active_per_slot. Откат. 409.

Транзакция дисциплинированно выполняла логически неверную последовательность — и откатывалась на constraint.

Фикс

Appointment должен покинуть slot1 до того как promote вставляет нового пациента:

  1. book_new_slot(slot2)

  2. move_appointment → slot2

  3. free_old_slot(slot1)

  4. promoteFromWaitlist(slot1)

8-й тест прошёл

Что означают 7 зелёных тестов

Тест проверяет поведение системы при конкретном начальном состоянии. Если в наборе тестов нет нужного domain state — класс ошибок невидим, сколько бы тестов ни прошло.

В данном случае критическое условие — пациент в вейтлисте — отсутствовало во всех семи тестах. promoteFromWaitlist` был no-op в каждом из них. Баг в порядке операций существовал с момента написания — просто не было состояния которое его активировало.

Атомарность транзакции гарантирует: либо все операции выполнятся, либо ни одна. Она не гарантирует что операции написаны в правильном порядке. Это разные гарантии — и мы путали их семь тестов подряд.

Скрытое предположение “Я решилf что если транзакция атомарна — порядок операций внутри неё можно не тестировать. На самом деле транзакция защищает от частичных обновлений, но не от логически неверного порядка внутри.”

Код проекта: GitHub

Из серии “Тихие отказы в тест-автоматизации” Разборы таких кейсов — в Telegram-канале Тесты как система

Tags:
-2
Comments0

Почему тесты проходят, но система всё равно сломана

Классы скрытых ошибок в QA automation, которые не приводят к падению CI

Пайплайн прошёл. Логи без ошибок. Значит всё работает.

Но в реальных QA automation системах это предположение часто не выдерживает проверки.

Тесты могут проходить, даже если система сломана.

И это не редкий edge case. Есть несколько типов проблем, которые не приводят к падению CI:

  • False positives — тест подтверждает поведение, которое уже не соответствует бизнес‑логике. Проверка формально зелёная, смысл потерян.

  • Missing assertions — тест проходит, потому что не проверяет ничего критичного.

  • Flaky suppression — флаки ретраят или игнорируют. Шум скрывает реальные проблемы, CI выглядит стабильным.

  • Duplicated execution — один и тот же набор тестов запускается несколько раз из‑за конфигурации runner'а.

  • Contract drift — API или поведение системы меняется, но тесты продолжают проверять старые ожидания. Пока не появится явный конфликт — всё зелёное.

В проекте была добавлена пагинация к одному из API эндпоинтов. До изменения ответ выглядел так:

json [{ "id": 1 }, { "id": 2 }]

После — так:

{ "data": [...], "total": 10, "page": 1, "limit": 20 }

API тесты не упали: они проверяли статус и структуру нового формата — всё корректно.

Я была уверена что если API возвращает 200 и схема верна — клиент получает данные.

Но в клиентском коде была строка:

cachedRows = Array.isArray(rows) ? rows : []

Для объекта Array.isArray возвращает false. Список записей стал пустым.

Формально всё работало корректно. Просто данных больше не было.Никаких ошибок в консоли. Никакого 500. Просто пустая страница.

CI остался зелёным — потому что API тесты проверяли API, а не то, как клиент использует ответ.

Дальше сработал каскад: fixture teardown тоже вызывал этот эндпоинт, получал объект вместо массива, не чистил данные — и следующие тесты падали с совершенно другой ошибкой, в совершенно другом файле.

Три теста упали из-за одного изменения shape ответа.

Ни один из них не указал на настоящую причину.

Почему CI это не ловит

CI отвечает на вопрос: «выполнились ли тесты без ошибок?»

Но не отвечает на: «имеют ли тесты смысл относительно текущей системы?»

CI реагирует только на падения. Он не знает про бизнес-инварианты, не отслеживает правильность выполнения и не видит contract drift.

Что с этим делают в зрелых системах

Начинают появляться дополнительные слои:

  • контрактные тесты (contract testing) — фиксируют ожидания потребителя API

  • явно наблюдаемость тестов — метрики не как %, а как сигналы поведения

  • контроль изменений API через diff-инструменты

Ни один из них не заменяет хорошие тесты. Но каждый закрывает слепое пятно, которое тесты не видят.

Финальный вывод

Тесты не доказывают, что система работает.

Они только доказывают, что система не сломалась определённым способом.

Признаки сбоя

  • CI зелёный

  • UI показывает пустой список

  • API возвращает 200

  • fixture teardown не чистил данные, занимал слот

Скрытое предположение

«Я решила что статус 200 означает, что потребитель по‑прежнему правильно читает ответ»

Как это выглядит в реальной системе

Contract drift — один из тех классов ошибок, которые можно воспроизвести намеренно. В проекте есть buggy branch именно с этим кейсом: API возвращает изменённый shape ответа, все API тесты зелёные, но клиентский код получает пустой список — без ошибок, без 500, просто тишина.

Код и структура проекта: GitHub

Из серии «Тихие отказы в тест-автоматизации»

Разборы таких кейсов с кодом — в Telegram-канале Тесты как система

Tags:
-1
Comments0

Почему зелёный CI не гарантирует, что система работает

Кейс из QA automation: как миграция на TypeScript привела к скрытому удвоению тестов без единого падения в CI

CI зелёный.

Тесты проходят.

Pull request’ы мерджатся.

Но система уже сломана.

И самое опасное — это не видно ни в логах, ни в отчётах CI.

В большинстве команд CI воспринимается как индикатор здоровья системы:

  • зелёный CI → всё работает

  • красный CI → есть проблема

Это удобная модель. Но она не всегда верна.

Контекст кейса

После миграции проекта с JavaScript на TypeScript мы заметили странное поведение:

  • CI стал выполняться почти в 2 раза дольше

  • тесты не падали

  • ошибок не было

  • метрики оставались “нормальными”

На первый взгляд — ничего критичного.

Что происходило на самом деле

Playwright начал подхватывать одновременно два набора тестов:

  • .spec.js

  • .spec.ts

В результате один и тот же тестовый набор запускался дважды.

Самое неприятное — CI не просто не показывал проблему. Он создавал иллюзию, что всё становится лучше: время выполнения росло постепенно, и это воспринималось как “нормальная деградация после миграции”.

Почему это было незаметно

Проблема усугублялась полным отсутствием сигналов:

  • CI оставался зелёным

  • тесты не фейлились

  • никаких warning’ов

  • никаких алертов

Единственный симптом — увеличение времени выполнения. Которое списали на “ну TypeScript, наверное тяжелее”.

Как проблема была обнаружена

Случайно. Ближе к завершению миграции, при удалении .js файлов, количество тестов внезапно сократилось примерно в два раза:

  • было ~240

  • стало ~120

До этого момента CI фактически выполнял двойную работу — без каких-либо признаков аномалии.

Root cause

Root cause оказался банальным — и именно поэтому его так долго не замечали.

В playwright.config.ts отсутствовал явный testMatch. Playwright по умолчанию подхватывает все файлы, соответствующие glob-паттерну — и .js, и .ts одновременно.

Фикс — одна строка:

testMatch: [‘**/*.spec.ts’]

Но чтобы до неё дойти, нужно было сначала понять, что вообще происходит.

Архитектурный вывод

Большинство проблем в тестовых системах не проявляются как падения.

Они проявляются как:

  • дублирование выполнения

  • скрытая деградация производительности

  • изменения в поведении runner’а без изменений в тестах

И у них нет алертов — потому что мы их не проектируем.

Например, в нашем случае проблему можно было бы поймать простым счётчиком discovered tests в CI.

Финальный вывод

CI — это не инструмент контроля качества системы. Это инструмент контроля того, что тесты не упали.

И если вы используете его как индикатор качества — вы просто получаете ложную уверенность быстрее.

CI отражает только одно: тесты выполнились без явных ошибок.

CI не отражает: тесты запустились правильно, в правильном количестве, с правильными предположениями об окружении.

Если система может быть “зелёной” и при этом работать некорректно — значит у вас есть статус выполнения, но нет наблюдаемости.

Как это выглядит в реальной системе

Именно этот кейс лёг в основу проекта, который я собирала как QA portfolio. В pipeline добавлен счётчик discovered tests: если количество отклоняется от ожидаемого, CI падает явно, а не молчит. Рядом — buggy branch с намеренно сломанной конфигурацией, чтобы можно было воспроизвести и починить самостоятельно.

Код и структура проекта: GitHub (https://github.com/Ariless/clinic-booking-api-tests)

Если собираешь QA портфолио или готовишься к техническому собеседованию — в Telegram-канале Тесты как система (https://t.me/qa_as_a_system) разбираю такие кейсы с кодом и объяснением: что показывать, как объяснять решения, какие находки работают на собеседовании.

Tags:
Total votes 3: ↑2 and ↓1+1
Comments6

Information

Rating
Does not participate
Registered
Activity

Specialization

Инженер по автоматизации тестирования, Инженер по ручному тестированию
Старший
Git
Docker
JavaScript
TypeScript