ТестированиеРучное тестированиеИнженер по ручному тестированию

На странице доступно последнее свободное место. Два пользователя почти одновременно отправляют одинаковый з...

На странице доступно последнее свободное место. Два пользователя почти одновременно отправляют одинаковый запрос бронирования:

# Клиент A
POST /reservations HTTP/1.1
Content-Type: application/json

{"itemId":"seat-42"}

# Клиент B
POST /reservations HTTP/1.1
Content-Type: application/json

{"itemId":"seat-42"}

Как построить ручную проверку, чтобы выявить ошибочное двойное бронирование?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

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

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

Исторический контекст

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

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

Постановка проблемы

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

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

Подробное решение

Сначала подготовьте контролируемое состояние: ровно одно место должно быть свободно, а у тестовых пользователей — независимые учётные записи и понятные идентификаторы операций. Зафиксируйте начальное состояние и критерий: после проверки в системе должна существовать максимум одна подтверждённая бронь на seat-42.

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

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

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

Тест должен учитывать предусмотренный контракт. Например, допустимыми могут быть один ответ 201 Created и один 409 Conflict, либо один успех и один контролируемый отказ. Недопустимы два подтверждения, две записи о владении одним местом или успешный ответ без фактической брони.

Важно отличать проверку конкурентности от обычной проверки нагрузки. Для выявления конкретной гонки нужны небольшое число участников, фиксированное состояние и синхронный запуск. Большое количество запросов полезно для поиска проблем под нагрузкой, но усложняет диагностику и не заменяет точечный сценарий.

Минимальная схема ручной проверки может выглядеть так:

1. Подготовить одно свободное место. 2. Авторизовать клиентов A и B. 3. Одновременно отправить запросы бронирования. 4. Зафиксировать ответы и время отправки. 5. Проверить число подтверждённых броней в системе. 6. Повторить сценарий несколько раз.

Если обнаружено двойное подтверждение, в дефекте укажите начальное состояние, идентификаторы клиентов и места, способ синхронизации, фактические ответы, время запросов и подтверждение состояния данных. Без этих сведений разработчику трудно отличить гонку от повторного запуска, задержки интерфейса или ошибки подготовки данных.

Ситуация из практики

В системе продажи билетов проверяли последнее место. Первый вариант — отправить запрос клиента A, дождаться ответа, затем отправить запрос клиента B. Он был простым и воспроизводимым, но проверял только последовательный сценарий; второй клиент закономерно получал отказ.

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

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

Что кандидаты часто упускают

  1. Достаточно ли отправить два запроса подряд с одного клиента?

Нет. Запросы, отправленные подряд, могут выполняться последовательно и не пересечься в критической секции. Для проверки гонки нужно обеспечить перекрытие операций или повторить сценарий с контролируемым параллельным запуском.

Если инструмент не поддерживает синхронизацию, можно использовать два браузерных сеанса и общий сигнал запуска, но это менее точно. В отчёте следует честно описать достигнутую степень параллельности и не выдавать приблизительную проверку за доказанное конкурентное выполнение.

  1. Как понять, что второй отказ является корректным, а не скрывает потерю данных?

Нужно проверить полный результат операции: статус брони, владельца места, доступность ресурса, историю заказа и, если применимо, состояние платежа. Один HTTP-статус или сообщение интерфейса недостаточны.

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

  1. Можно ли считать дефектом два успешных ответа, если в итоге в базе осталась одна бронь?

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

Нужно сопоставить семантику ответа с контрактом операции. Если система допускает предварительное резервирование, а окончательная фиксация выполняется отдельным шагом, два промежуточных успеха сами по себе могут быть допустимы; тогда проверять нужно уникальность окончательного подтверждения и отсутствие некорректных побочных эффектов.