Изображение сгенерировано Еленой Синдяшкиной
Изображение сгенерировано Еленой Синдяшкиной

Как вы относитесь к cucumber? Не любите? Просто у вас рецепт не тот! А если любите, то полюбите ещё больше, когда я расскажу о замороженном кукумбере. Уж мороженое все любят. Меня зовут Юра Синдяшкин, я работаю в М.Видео-Эльдорадо и сегодня покажу, как кукумбер можно сделать ещё более удобным для автотестера.

В подходе BDD (Behaviour Driven Development — «Разработка через поведение») прекрасно все: читаемость сценария, низкий порог входа для начинающего автоматизатора, сценарий мчится на всех парах до своего падения. Если повезёт, то падение произойдёт через несколько недель или месяцев, когда в продукт внесут изменения. И вот тут перед автотестером возникает задача: как после падения быстрее внести правки в код теста?

Когда речь идёт о бэкэнде, артефакты теста можно отобразить в отчёте в удобном виде «Было-Стало», и тогда тест править легко. Но что делать, когда падает UI тест и лучшее, что у вас есть это скриншот? Проходить тест руками? Это не наш метод!

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

Решение: Хуки кукумбера. А именно хук «@AfterStep», который выполняется после каждого шага. Далее я покажу реализованное мной на Java решение, которым пользуется несколько команд автотестеров в М.Видео-Эльдорадо.

В хук передаётся состояние объекта Scenario, в котором хранится состояние сценария: упал или ещё всё впереди. Напишем процедуру, она будет вызываться в хуке. Будем менять состояние переменной breakpointState в соответствии с состоянием сценария, чтобы управлять заморозкой.

public static volatile String breakpointState = STATE_RESUME;

public static void handleBreakpointActions(boolean shouldBeStopped) {

     if (shouldBeStopped && isBreakpointFeatureOn()) {

         breakpointState = STATE_PAUSE;

     }

     if (breakpointState.equals(STATE_PAUSE) || breakpointState.equals(STATE_ONE_STEP)) {

         breakpointState = STATE_PAUSE;

         makePause();

     }

     else{

         waitForMs(waitBetweenSteps);

     }

}
Зачем дополнительная переменная breakpointState, если всё есть в Scenario? Не всё. Состояний теста будет три: STATE_RESUME (ход на всех парах), STATE_ONE_STEP (выполнить один шаг), STATE_PAUSE (заморозить тест). И управлять состояниями будем из разных потоков, где нет объектов Scenario. Об этом позже.

Если тест находится в состоянии STATE_PAUSE, то выполняем бесконечный цикл ожидания

private static void makePause() {

     while (breakpointState.equals(STATE_PAUSE)) {

         waitForMs(1000);

     }

}

За время заморозки теста можно во все ещё открытом браузере проверить сломанный локатор, найти новый и исправить код теста.

Как же прервать бесконечное ожидание? Хочется ведь завершать тест обычным способом со сбором всех артефактов теста. Для управления замороженным сценарием можно использовать любой способ общения микросервисов. Я выбрал rest-запросы инструмента Spark. Он поднимается в отдельном потоке при старте теста и слушает три эндпоинта: «pause», «resume», «onestep».

Если сообщение к нему приходит, он меняет состояние breakpointState. Кто будет посылать сообщения по этим адресам? Команды поступают из двух пультов управления на выбор. Первый пульт управления - плагин для IntelliJ IDEA с красивыми кнопками и шорткатом «shift SPACE».

Инструкция по написанию плагина выходит за рамки статьи, поэтому приведу только фрагмент, относящийся к сути статьи. Это фрагмент файла plugin.xml, где я регистрирую действия плагина.

  <actions>

<action id="breakpoint.oneStep" class="ru.mvideo.OneStepButton"

text="One Step" description="Do one step" icon="/icons/oneStep.png">

<add-to-group group-id="ToolbarRunGroup" anchor="last" />

</action>

<action id="breakpoint.pause" class="ru.mvideo.PauseButton"

text="Pause" description="Pause test" icon="/icons/pause.png">

<add-to-group group-id="ToolbarRunGroup" anchor="last" />

</action>

<action id="breakpoint.key" class="ru.mvideo.KeyControl">

<keyboard-shortcut first-keystroke="shift SPACE" keymap="$default" />

</action>

<action id="breakpoint.resume" class="ru.mvideo.ResumeButton"

text="Resume" description="Resume test" icon="/icons/resume.png">

<add-to-group group-id="ToolbarRunGroup" anchor="last" />

</action>

</actions>

Кнопки плагина выполняют простой http-запрос к спарку, в результате чего состояние теста меняется при следующем срабатывании хука «AfterStep». Кнопками удобно пользоваться, когда хочется видеть шаги сценария по мере прохождения. Что, если хочется видеть действия браузера и останавливать сценарий из браузера? Для этого есть второй пульт управления.

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

pauseElement.addEventListener("click", function (){ sendBreakpointActionFunction(PAUSE_ACTION); }, true);

Итак, итог: кукумбер-тест можно замораживать по воле тестера, а также автоматически в случае падения шага до завершения теста! Как результат — можно в открытом браузере проверить локаторы и данные в том состоянии страницы, которое привело к падению.

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

Комментарии (0)