Первый опыт работы с EventBus шиной событий | Автоматизация тестирования на Java
Всем привет! Относительно недавно, мне повезло столкнуться с задачей автоматизации, связанной с шиной событий EventBus. Задача довольно интересная, поэтому хочу поделиться своим решением, вдруг помогу кому-нибудь)
Что такое EventBus
Скорее всего если вы читаете эту статью, то уже имеете примерное представление, что такое EventBus. Но все равно приведу определение из интернета:
Event Bus — это паттерн проектирования, обеспечивающий взаимодействие между слабо связанными компонентами по принципу «публикатор события-подписчик на событие».
Компонент может отправить сообщение в шину событий, не зная конечного получателя. С другой стороны, компонент может прослушать сообщение в шине событий и решить, что с ним делать, не располагая информацией об его отправителе. Посредством данного паттерна независимые компоненты могут взаимодействовать между собой, не зная друг друга.
Однако, для лучшего понимания написанного ниже, лучше пробежаться по следующим источникам (хотя-бы, по первым четырем):
Приложение для демонстрации
Представим, что мы пилим очень нужное приложение, где пользователь может накачать свой указательный палец правой руки.
Переход по экранам приложения «Накачай свой палец»
Пользователь переходит на главную страницу приложения, кликает на кнопку «Старт», далее кнопка меняется на кнопку «Жми», а снизу количество кликов, которое генерируется рандомно. Счетчик уменьшается с каждым нажатием. После того как пользователь прокликал весь счетчик, приложение выводит надпись «Поздравляю, ты псих!» и отображаются кнопки «да» / «нет»…назовем их так))
События в нашем приложении
Мы знаем, что при клике на кнопку «Старт» в шину событий EventBus отправляется событие начала игры. Тип этого события "start_crazy_game_event”
. Детали этого события:
{
status: start,
allSteps : 99
}
При клике на кнопку «Жми» отправляется событие прогресса с номером текущего шага. При этом, если номер текущего шага равен общему количеству, то отправляется событие завершения игры и выводится последний экран приложения. Тип такого события "progress_crazy_game_event”
. Детали этого события:
{
status: progress,
currentStep : 1,
allSteps : 99
}
Ну, а кнопки на финальном экране приложения — события согласия / не согласия пользователя с типом "agree_result_crazy_game_event”
и деталями:
{
agree: true
}
Отправку события, а именно вызов метода dispatch, можно осуществить через консоль в DevTools. Из источников в начале статьи лично я узнал от разработчика мы знаем, что объект EventBus нужно искать в объекте window. Выглядит это следующим образом:
Пример отправки события методом dispatchEvent в DevTools console
Как отправить событие в EventBus используя Java
Первое, что приходит в голову при попытке это автоматизировать, использование метода executeJavaScript в Selenide. Конечно, одним методом будет сложно ограничится, если есть желание написать что-то более менее удобное. Поэтому хочу предложить свое решение в моем Github.
Кратенько опишу некоторые моменты.
Основной класс который мы будем использовать в самих тестах это EventBusHelper
. Как я и говорил выше, Selenide.executeJavaScript () это лишь малая часть:
private Object executeToEventBus(String content) {
return Selenide.executeJavaScript(content);
}
Нам нужно грамотно передать в этот метод строку, описывающую обращение к EventBus через консоль. Эту задачу выполним в методе runEventBusMethodWithParams()
:
private Object runEventBusMethodWithParams(EventBusMethodEnum eventBusMethod, String eventBusMethodParameters) {
return executeToEventBus(String.format("return window.%s.%s(%s);", EVENT_BUS, eventBusMethod.getEventBusMethod(), eventBusMethodParameters));
}
Из пояснений:
Для того что бы метод успешно выполнился в консоли необходимо добавлять «return» в начале строки — »return window.crazy_game…
» и т.д. Но это нужно именно для executeJavaScript метода, а не когда мы вручную вызываем метод в console в DevTools.
Такие вещи как типы событий, названия методов и имя самого объекта EventBus (EVENT_BUS) удобнее вынести в константы. Смотрите на перечисления EventBusEventTypeEnum.java
и EventBusMethodEnum.java
Далее, имея метод runEventBusMethodWithParams()
мы можем описать уже сам EventBus метод dispatchEvent (event):
public void runDispatchEventMethod(Event event) {
runEventBusMethodWithParams( DISPATCH_EVENT_METHOD, getDispatchEventMethodParams(event));
}
Из интересного тут это параметр метода — объект класса Event
. Сам класс Event
описан двумя полями это type
(тип события) и detail
(описание события).
Если класс Event
скорее всего у нас с вами будет общий, то описание класса Detail
зависит от проекта, у меня это:
public class Detail {
private String status;
private Integer currentStep;
private Integer allSteps;
private Boolean agree;
}
А, ну и метод getDispatchEventMethodParams(Event event)
нужен, чтобы собрать финальную строку с параметрами eventBus метода dispatchEvent.
Внутри метода, в зависимости от типа события, собирается объект события из шаблона:
if (eventType.equals(START_CRAZY_GAME_EVENT.getEvent())) {
eventTemplate = getStartEventDetailTemplate(event.getDetail());
} else if (eventType.equals(AGREE_RESULT_CRAZY_GAME_EVENT.getEvent())) {
eventTemplate = getAgreeEventDetailTemplate(event.getDetail());
} else if (eventType.equals(PROGRESS_CRAZY_GAME_EVENT.getEvent())) {
eventTemplate = getProgressEventDetailTemplate(event.getDetail());
}
В качестве шаблона я использовал JavaScript файлы, к примеру src/main/resources/eventBus/eventBusAgreeResultEventTemplate.js
. Пример содержимого одного из таких файлов:
{
agree : __DETAIL_AGREE_STATUS_VALUE__
}
Нужное значение для DETAIL_AGREE_STATUS_VALUE
подставляется простой заменой подстроки:
private String getAgreeEventDetailTemplate(Detail detail) {
return readFile("eventBus/eventBusAgreeResultEventTemplate.js")
.replace("__DETAIL_AGREE_STATUS_VALUE__", String.valueOf(detail.getAgree()));
}
Думаю, можно придумать решение изящнее) Но это работает и пока меня устраивает.
Как убедится, что событие отправлено
У меня возникла следующая проблема — как убедиться, что метод отработал во время теста. Если метод в консоли отработает некорректно или вообще не отработает, то мы не узнаем о проблеме вовремя. А проблему покажет следующий шаг теста, не найдет к примеру какой-нибудь элемент, который должен был появиться после отправки события в EventBus и упадет. Это не совсем информативно.
Но решение есть.
Event Bus не хранит историю всех отправленных событий, а хранит только список последних отправленных событий конкретного типа.
То есть, получив объект последнего события, мы можем проверить тип и даже сам объект. Как я понял, так как EventBus это паттерн, то разработчик сам решает какая будет реализация. Мне повезло и у меня была возможность получать крайние события определенных типов событий через объект lastEventValues
объекта eventBus. Кстати, именно, чтобы получать возвращаемое значение мы и добавляли «return» в начале входной строки метода executeJavaScript.
Это выглядит следующим образом: window.crazy_game_event_bus.lastEventValues
Объект lastEventValues
представляет собой мапу объектов, где один из ключей это список событий, а значение это другая мапа, где ключи это типы событий, а значения это detail
событий. И это надо было как то парсить. Для решения этой задачи есть класс ru/example/eventBus/providers/EventBusObjectProvider.java
.
Из интересного в этом классе есть метод getEventObjDetail
:
private Detail getEventObjDetail(Map event) {
var detail = new Detail();
if ((event.containsKey(ALL_STEPS.getFieldName()))) {
detail.setAllSteps((Integer) event.get(ALL_STEPS.getFieldName()));
}
if ((event.containsKey(STATUS.getFieldName()))) {
detail.setStatus((String) event.get(STATUS.getFieldName()));
}
if ((event.containsKey(CURRENT_STEP.getFieldName()))) {
detail.setCurrentStep((Integer) event.get(CURRENT_STEP.getFieldName()));
}
if ((event.containsKey(AGREE_STATUS.getFieldName()))) {
detail.setAgree((Boolean) event.get(AGREE_STATUS.getFieldName()));
}
return detail;
}
В этом методе собирается объект Detail
, чтобы в последующем можно было сравнить объект Detail
из отправленного ранее события и объект Detail
полученный из списка последних событий. Недостаток в том, что нужно описать отдельно каждое поле объекта detail
, как это сделано тут ru/example/eventBus/enums/DetailFieldEnum.java
. Но с другой стороны, маловероятно что этот объект у вас будет объемным.
Таким образом мы можем убедиться, что событие отправлено успешно.
Но повторюсь, если у вас нет этой возможности, то можете просто отправлять событие или же попросить разработчика вам помочь, думаю для него это будет не сложно. Так что, эта проверка опциональна.
Заключение
Из источников, с описанием EventBus становится понятно, что реализация данного паттерна может сильно отличаться в разных проектах у разных разработчиков. Но, надеюсь моя реализация поможет вам чуть быстрее вникнуть в тему и сообразить решение вашей задачи. Естественно, я не претендую на экспертность в данной теме и буду раз замечаниям и мнениям старших и опытных коллег автотестеров и разработчиков)) Мое решение рабочее, поэтому решил им поделится, но ваша критика поможет мне его улучшить!
Пытаюсь вести блог https://t.me/qanva_blog, заходите)