ТестированиеМобильное тестированиеИнженер по мобильному тестированию

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

Тестировщик замечает: после быстрого обновления экрана мобильное приложение иногда показывает неактуальные данные. Как локализовать проблему с конкурирующими запросами?

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

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

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

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

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

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

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

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

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

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

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

Сначала нужно воспроизвести гонку управляемо:

  1. Записать для каждого запроса идентификатор, параметры, время отправки, время получения ответа и номер версии данных.
  2. Запустить два или несколько обновлений одного ресурса с минимальным интервалом.
  3. Задержать первый ответ дольше второго или использовать прокси, mock-сервер и управляемый сетевой профиль.
  4. Проверить, какой ответ фактически отобразился после завершения всех запросов.
  5. Повторить сценарий при смене сети, тайм-ауте, повторной отправке и возврате приложения на передний план.

Наиболее простой клиентский предохранитель — номер поколения состояния. Перед началом запроса приложение увеличивает номер, а ответ применяет только тогда, когда его номер совпадает с текущим. Другой вариант — сравнивать серверную версию, ревизию или время изменения ресурса; это надёжнее, когда данные могут изменяться не только из текущего клиента.

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

Важно определить семантику актуальности. Для поиска обычно действует правило «побеждает последний пользовательский ввод», а для финансовой операции нельзя просто отбрасывать ответы: нужна серверная идемпотентность, версия объекта или явная обработка конфликта.

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

В приложении экран списка заказов обновлялся при открытии и по жесту обновления. При медленной сети пользователь иногда видел старый статус заказа. Логи показали: запрос R1 отправлен первым, R2 — вторым; R2 завершился раньше, но затем R1 перерисовал экран.

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

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

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

  1. Достаточно ли сравнить время завершения запросов?

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

  1. Почему тест с одной последовательной загрузкой не выявляет дефект?

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

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

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