Состояния и переходы
Взял жизненный цикл заказа: семь состояний, шесть событий, восемь разрешённых переходов. Построил матрицу «состояние на событие» - в ней 42 ячейки. Разрешённых из них восемь, а НЕразрешённых тридцать четыре. И вот эти тридцать четыре обычно никто не проверяет.
Стержень: у сущности с жизненным циклом запрещённых переходов всегда в разы больше, чем разрешённых, и проверять надо именно их.
// Формулировки: «как тестировать сущность с жизненным циклом?», «что такое покрытие переходов?», «зачем проверять недопустимые переходы?».
Состояния, переходы, уровни покрытия
Модель состоит из состояний, событий и переходов: заказ «новый», приходит событие «оплатить», заказ становится «оплачен». Рисуют это схемой или таблицей, и уже сама рисовка обычно выявляет пару неописанных случаев.
Покрытий два основных. Простое: пройти каждый переход хотя бы раз - в моём примере это восемь тестов. Расширенное: пройти каждую ПАРУ подряд идущих переходов; я посчитал такие пары, их вышло шесть. Меньше, чем самих переходов, потому что из конечных состояний - отменён, возвращён, вручён - дальше идти уже некуда.
// Зачем нужен второй уровень: ошибки часто сидят не в самом переходе, а в последовательности. Оплата после возврата, отмена после отправки, вторая оплата подряд - каждое действие по отдельности корректно, а вместе даёт состояние, которого быть не должно.
жизненный цикл заказа:
состояний 7, событий 6
матрица состояние x событие: 7 x 6 = 42 ячейки
разрешённых переходов: 8
НЕразрешённых сочетаний: 34
покрытие каждого перехода: 8 тестов
покрытие пар переходов: 6 тестов- покрытие переходов
- каждый переход пройден хотя бы раз
- покрытие пар
- каждая пара подряд идущих переходов пройдена
Матрица и запрещённые переходы
Матрица строится механически: по строкам состояния, по столбцам события, в ячейке - что должно произойти. Из моих 42 ячеек заполнено переходами восемь, а тридцать четыре означают «так делать нельзя».
Именно эти тридцать четыре и дают самые интересные баги. Мало убедиться, что система откажет. Проверяют КАК она откажет: понятной ошибкой, без изменения состояния, без побочных действий. Худший исход - система переход выполнила, потому что проверку писали только для правильных случаев.
// Отдельный класс атак сюда же: обойти интерфейс и послать событие прямо на сервер, минуя кнопки, - через API (application programming interface), то есть тот же адрес, куда стучится приложение. Кнопка «оплатить» на отменённом заказе не показывается, но запрос-то отправить можно. Проверка состояния обязана быть на сервере, а не в вёрстке.
- матрица переходов
- состояния по строкам, события по столбцам, пустая ячейка = запрещено
Откуда приходят события и что ломается на гонках
События приходят не только от пользователя. Их шлют планировщики - «отменить неоплаченный через сутки», внешние системы - «платёж подтверждён банком», администраторы вручную, и повторные сообщения из очередей. В матрице такие источники учитывают наравне с кнопками интерфейса.
Отсюда самое неприятное - одновременные события. Пользователь нажал «отменить», и в ту же секунду пришло подтверждение платежа. Что должно получиться? Требования на такое обычно молчат, а система в этот момент делает что-то на своё усмотрение.
// Проверяют это двумя способами: параллельными запросами в тесте и повторной отправкой одного и того же события. Второе особенно важно, потому что очереди доставляют сообщения повторно штатно, и обработчик перехода обязан быть устойчивым к повтору - принять его один раз и не свалиться.
- источники событий
- пользователь, планировщик, внешняя система, повтор из очереди
Как отвечать: «Как тестировать сущность с жизненным циклом?»
Начинаю с модели: выписываю состояния, события и разрешённые переходы, потом строю матрицу «состояние на событие». На жизненном цикле заказа у меня вышло семь состояний, шесть событий и восемь разрешённых переходов - при этом ячеек в матрице сорок две, значит тридцать четыре сочетания запрещены, и они тоже требуют проверки. Дальше два уровня покрытия. Простое: пройти каждый разрешённый переход, это восемь тестов. Расширенное: пройти каждую пару подряд идущих переходов, их я насчитал шесть - меньше, чем переходов, потому что из конечных состояний идти уже некуда. Пары нужны потому, что баги часто сидят в последовательности: оплата после возврата, вторая оплата подряд. У запрещённых переходов проверяю не сам факт отказа, а его качество: понятная ошибка, состояние не изменилось, побочных действий не произошло. И обязательно посылаю событие напрямую в API мимо интерфейса, потому что кнопку скрыть легко, а проверка должна быть на сервере.
Почему это сильный ответ: есть модель, два уровня покрытия с числами, отдельно проговорены запрещённые переходы и обход интерфейса - то, что пропускает большинство.
На чём валят
- −Проверить только правильный путь. Запрещённых сочетаний в моей матрице оказалось 34 против 8 разрешённых.
- −Проверять переходы по одному и не проверять последовательности. Оплата после возврата - каждое действие по отдельности законно.
- −Считать отказ достаточным. Надо ещё убедиться, что состояние не изменилось и побочных действий не было.
- −Проверять только через интерфейс. Кнопку скрыли, а запрос отправить можно - проверка обязана быть на сервере.
- −Забыть про события от планировщиков и повторы из очереди. Обработчик перехода обязан выдерживать повтор.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 13, остальные разбираются в тренажёре.
- Что моделирует техника «диаграмма состояний-переходов» (state transition)?A)Состояния системы, события и переходы между нимиB)Комбинации входных условий и соответствующих им действий в бизнес-правилеC)Границы допустимых диапазонов входных полей и значения вокруг нихD)Порядок выполнения шагов в одном линейном тест-сценарии от начала до конца
показать ответ и разбор
+A)Состояния системы, события и переходы между ними// разбор: State-transition-тестирование описывает объект как набор состояний (например, заказ: создан → оплачен → отправлен → доставлен), события, вызывающие смену состояния (оплатить, отменить), и допустимые переходы между ними. Техника нужна там, где реакция системы зависит не только от входа, но и от того, в каком состоянии она сейчас: «отменить» работает для созданного заказа, но не для доставленного. Из диаграммы выводят тесты: пройти валидные пути, проверить запрещённые переходы, покрыть все переходы и/или их пары.
- В каком случае уместно применять тестирование по состояниям и переходам?A)Когда у входного поля много допустимых значений и нужно покрыть их представителямиB)Когда поведение зависит от текущего состоянияC)Когда результат зависит от комбинации нескольких независимых условий одновременноD)Когда нужно оценить, выдержит ли система заданное число одновременных пользователей
показать ответ и разбор
+B)Когда поведение зависит от текущего состояния// разбор: Признак, что нужна эта техника, — наличие у объекта «памяти»: результат действия определяется не только его параметрами, но и предысторией, свёрнутой в текущее состояние. Кнопка «отправить» переводит заказ из «оплачен» в «отправлен», но в состоянии «отменён» то же действие должно отклоняться. Такие объекты — заказы, платежи, пользовательские сессии, автоматы (банкомат, турникет), подписки. Для полей без состояния (валидация одного числа) техника избыточна — там хватает классов и границ. Сигнал к применению: «что можно сделать» зависит от «что было раньше».
- Почему при тестировании по состояниям проверяют не только разрешённые, но и запрещённые переходы?A)Запрещённые переходы проверяют в системах безопасности, а в остальных это лишняя работаB)Их проверяют, чтобы измерить, насколько быстро система отклоняет некорректные событияC)Частый баг — система принимает событие в состоянии, где оно недопустимоD)Запрещённые переходы проверяют вместо разрешённых, если разрешённых слишком много
показать ответ и разбор
+C)Частый баг — система принимает событие в состоянии, где оно недопустимо// разбор: Разрешённые переходы подтверждают, что система корректно движется по «счастливому пути». Но не менее важно, что она отвергает недопустимые действия: отменить уже доставленный заказ, снять деньги с закрытого счёта, залогиниться в заблокированной сессии. Реальные дефекты часто именно здесь — код обрабатывает ожидаемые события, но забывает заблокировать неожиданные, и система переходит в некорректное состояние или выполняет запретную операцию. Поэтому тесты покрывают и «что должно произойти», и «что должно быть отклонено» для событий, недопустимых в данном состоянии.
- Что покрывает базовое (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 по риску.
- Банкомат блокирует карту после 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 с граничной проверкой порога.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.