сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Теория тестирования

Состояния и переходы

Тестирование переходов состояний

Взял жизненный цикл заказа: семь состояний, шесть событий, восемь разрешённых переходов. Построил матрицу «состояние на событие» - в ней 42 ячейки. Разрешённых из них восемь, а НЕразрешённых тридцать четыре. И вот эти тридцать четыре обычно никто не проверяет.

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

// Формулировки: «как тестировать сущность с жизненным циклом?», «что такое покрытие переходов?», «зачем проверять недопустимые переходы?».

Состояния, переходы, уровни покрытия

Модель состоит из состояний, событий и переходов: заказ «новый», приходит событие «оплатить», заказ становится «оплачен». Рисуют это схемой или таблицей, и уже сама рисовка обычно выявляет пару неописанных случаев.

Покрытий два основных. Простое: пройти каждый переход хотя бы раз - в моём примере это восемь тестов. Расширенное: пройти каждую ПАРУ подряд идущих переходов; я посчитал такие пары, их вышло шесть. Меньше, чем самих переходов, потому что из конечных состояний - отменён, возвращён, вручён - дальше идти уже некуда.

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

жизненный цикл заказа:
  состояний 7, событий 6
  матрица состояние x событие: 7 x 6 = 42 ячейки
  разрешённых переходов:        8
  НЕразрешённых сочетаний:     34

  покрытие каждого перехода:     8 тестов
  покрытие пар переходов:        6 тестов
покрытие переходов
каждый переход пройден хотя бы раз
покрытие пар
каждая пара подряд идущих переходов пройдена

Матрица и запрещённые переходы

Матрица строится механически: по строкам состояния, по столбцам события, в ячейке - что должно произойти. Из моих 42 ячеек заполнено переходами восемь, а тридцать четыре означают «так делать нельзя».

Именно эти тридцать четыре и дают самые интересные баги. Мало убедиться, что система откажет. Проверяют КАК она откажет: понятной ошибкой, без изменения состояния, без побочных действий. Худший исход - система переход выполнила, потому что проверку писали только для правильных случаев.

// Отдельный класс атак сюда же: обойти интерфейс и послать событие прямо на сервер, минуя кнопки, - через API (application programming interface), то есть тот же адрес, куда стучится приложение. Кнопка «оплатить» на отменённом заказе не показывается, но запрос-то отправить можно. Проверка состояния обязана быть на сервере, а не в вёрстке.

матрица переходов
состояния по строкам, события по столбцам, пустая ячейка = запрещено

Откуда приходят события и что ломается на гонках

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

Отсюда самое неприятное - одновременные события. Пользователь нажал «отменить», и в ту же секунду пришло подтверждение платежа. Что должно получиться? Требования на такое обычно молчат, а система в этот момент делает что-то на своё усмотрение.

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

источники событий
пользователь, планировщик, внешняя система, повтор из очереди

Как отвечать: «Как тестировать сущность с жизненным циклом?»

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

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

На чём валят

  • Проверить только правильный путь. Запрещённых сочетаний в моей матрице оказалось 34 против 8 разрешённых.
  • Проверять переходы по одному и не проверять последовательности. Оплата после возврата - каждое действие по отдельности законно.
  • Считать отказ достаточным. Надо ещё убедиться, что состояние не изменилось и побочных действий не было.
  • Проверять только через интерфейс. Кнопку скрыли, а запрос отправить можно - проверка обязана быть на сервере.
  • Забыть про события от планировщиков и повторы из очереди. Обработчик перехода обязан выдерживать повтор.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 13, остальные разбираются в тренажёре.

  1. #state_transition1 / 5
    Что моделирует техника «диаграмма состояний-переходов» (state transition)?
    A)Состояния системы, события и переходы между ними
    B)Комбинации входных условий и соответствующих им действий в бизнес-правиле
    C)Границы допустимых диапазонов входных полей и значения вокруг них
    D)Порядок выполнения шагов в одном линейном тест-сценарии от начала до конца
    показать ответ и разбор
    +A)Состояния системы, события и переходы между ними

    // разбор: State-transition-тестирование описывает объект как набор состояний (например, заказ: создан → оплачен → отправлен → доставлен), события, вызывающие смену состояния (оплатить, отменить), и допустимые переходы между ними. Техника нужна там, где реакция системы зависит не только от входа, но и от того, в каком состоянии она сейчас: «отменить» работает для созданного заказа, но не для доставленного. Из диаграммы выводят тесты: пройти валидные пути, проверить запрещённые переходы, покрыть все переходы и/или их пары.

  2. #state_transition2 / 5
    В каком случае уместно применять тестирование по состояниям и переходам?
    A)Когда у входного поля много допустимых значений и нужно покрыть их представителями
    B)Когда поведение зависит от текущего состояния
    C)Когда результат зависит от комбинации нескольких независимых условий одновременно
    D)Когда нужно оценить, выдержит ли система заданное число одновременных пользователей
    показать ответ и разбор
    +B)Когда поведение зависит от текущего состояния

    // разбор: Признак, что нужна эта техника, — наличие у объекта «памяти»: результат действия определяется не только его параметрами, но и предысторией, свёрнутой в текущее состояние. Кнопка «отправить» переводит заказ из «оплачен» в «отправлен», но в состоянии «отменён» то же действие должно отклоняться. Такие объекты — заказы, платежи, пользовательские сессии, автоматы (банкомат, турникет), подписки. Для полей без состояния (валидация одного числа) техника избыточна — там хватает классов и границ. Сигнал к применению: «что можно сделать» зависит от «что было раньше».

  3. #state_transition3 / 5
    Почему при тестировании по состояниям проверяют не только разрешённые, но и запрещённые переходы?
    A)Запрещённые переходы проверяют в системах безопасности, а в остальных это лишняя работа
    B)Их проверяют, чтобы измерить, насколько быстро система отклоняет некорректные события
    C)Частый баг — система принимает событие в состоянии, где оно недопустимо
    D)Запрещённые переходы проверяют вместо разрешённых, если разрешённых слишком много
    показать ответ и разбор
    +C)Частый баг — система принимает событие в состоянии, где оно недопустимо

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

  4. #state_transition4 / 5
    Что покрывает базовое (0-switch) покрытие переходов и что добавляет 1-switch?
    A)0-switch покрывает состояния, а 1-switch — события; вместе они дают полную диаграмму
    B)0-switch проверяет валидные переходы, а 1-switch — запрещённые переходы системы
    C)1-switch означает один тест на всю диаграмму, а 0-switch — ни одного
    D)0-switch — каждый переход хотя бы раз; 1-switch дополнительно покрывает пары последовательных переходов
    показать ответ и разбор
    +D)0-switch — каждый переход хотя бы раз; 1-switch дополнительно покрывает пары последовательных переходов

    // разбор: Покрытие переходов измеряет полноту тестов по диаграмме. 0-switch (покрытие всех переходов) требует, чтобы каждый допустимый переход сработал в тестах хотя бы раз — базовый уровень, ловит переходы, которые вообще не работают. 1-switch идёт дальше: покрывает все пары последовательных переходов (переход A, затем сразу B), вскрывая дефекты, зависящие от того, каким переходом система попала в текущее состояние. Каждый следующий уровень (n-switch) покрывает более длинные цепочки ценой роста числа тестов. На практике чаще ограничиваются 0- или 1-switch по риску.

  5. #state_transition5 / 5
    Банкомат блокирует карту после 3 неверных PIN. Как это тестируют по состояниям?
    A)Моделируют состояния по числу попыток; событие «неверный PIN» ведёт к блокировке на 3-й
    B)Проверяют лишь, что при верном PIN карта разблокируется — путь к блокировке несуществен
    C)Достаточно одного теста с тремя неверными PIN подряд без учёта промежуточных состояний
    D)Моделируют состояния по времени суток, так как блокировка зависит от часа операции
    показать ответ и разбор
    +A)Моделируют состояния по числу попыток; событие «неверный PIN» ведёт к блокировке на 3-й

    // разбор: Систему описывают состояниями: 0 ошибок → 1 → 2 → заблокирован. Событие «неверный PIN» инкрементирует счётчик, а на третьем срабатывании выполняет переход в «заблокирован». Тесты: пройти путь 1-я, 2-я, 3-я ошибка и убедиться, что блокировка наступает ровно на третьей, а не на второй или четвёртой (граница счётчика). Отдельно — что верный PIN на 1-й/2-й ошибке сбрасывает счётчик (переход назад), и что в состоянии «заблокирован» дальнейшие попытки отклоняются. Это сочетает state-transition с граничной проверкой порога.

дальше

Теорию прочитали. Навык ставится повторением

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