First experience with EventBus event bus

Hi all! Relatively recently, I was lucky enough to encounter an automation task related to the EventBus event bus. The problem is quite interesting, so I want to share my solution in case I help someone)

What is EventBus

Most likely, if you are reading this article, you already have a rough idea of ​​what EventBus is. But I’ll still give a definition from the Internet:

Event Bus is a design pattern that provides interaction between loosely coupled components according to the “event publisher-event subscriber” principle.

A component can send a message to the event bus without knowing the final recipient. On the other hand, a component can listen to a message on the event bus and decide what to do with it without knowing its sender. Through this pattern, independent components can interact with each other without knowing each other.

However, for a better understanding of what is written below, it is better to go through the following sources (at least the first four):

Application for demonstration

Let's imagine that we are creating a very necessary application where the user can pump up his right index finger.

Navigating through application screens "Pump up your finger"

Navigate through the screens of the “Pump up your finger” application

The user goes to the main page of the application, clicks on the “Start” button, then the button changes to the “Click” button and below is the number of clicks that is generated randomly. The counter decreases with each press. After the user has clicked through the entire counter, the application displays the message “Congratulations, you are crazy!” and the “yes” / “no” buttons are displayed..let’s call them that))

Events in our application

We know that when you click on the “Start” button, EventBus is sent to the event bus start event games. Type of this event “start_crazy_game_event”. Details of this event:

{
	status: start,
	allSteps : 99
}

When you click on the “Press” button, it sends event progress with the number of the current step. In this case, if the number of the current step is equal to the total number, then the game completion event is sent and the last screen of the application is displayed. Type of such event “progress_crazy_game_event”. Details of this event:

{
	status: progress,
	currentStep : 1,
	allSteps : 99
}

Well, the buttons on the final screen of the application – consent events / user does not agree with the type “agree_result_crazy_game_event” and details:

{
	agree: true
}

Sending an event, namely calling a method dispatch, can be done through the console in DevTools. From the sources at the beginning of the article I personally found out from the developer we know that the EventBus object must be looked for in the object window. It looks like this:

An example of sending an event using the dispatchEvent method in the DevTools console

An example of sending an event using the dispatchEvent method in the DevTools console

How to send an event to EventBus using Java

The first thing that comes to mind when trying to automate is using a method executeJavaScript V Selenide. Of course, it will be difficult to limit yourself to one method if you want to write something more or less convenient. Therefore, I want to offer my solution in my Github.

I will briefly describe some points.

The main class that we will use in the tests themselves is EventBusHelper. As I said above, Selenide.executeJavaScript() is only a small part:

private Object executeToEventBus(String content) {
   return Selenide.executeJavaScript(content);
}

We need to correctly pass a string to this method that describes the call to EventBus via the console. We will perform this task in the method runEventBusMethodWithParams():

private Object runEventBusMethodWithParams(EventBusMethodEnum eventBusMethod, String eventBusMethodParameters) {
   return executeToEventBus(String.format("return window.%s.%s(%s);", EVENT_BUS, eventBusMethod.getEventBusMethod(), eventBusMethodParameters));
}

From the explanations:

In order for the method to be executed successfully in the console, you must add “return” at the beginning of the line – “return window.crazy_game…” etc. But this is necessary precisely for executeJavaScript method, rather than when we manually call the method in the console in DevTools.

It is more convenient to put things like event types, method names and the name of the EventBus object itself (EVENT_BUS) into constants. Look at the transfers EventBusEventTypeEnum.java And EventBusMethodEnum.java

Next, having a method runEventBusMethodWithParams() we can describe the EventBus method itself dispatchEvent(event):

public void runDispatchEventMethod(Event event) {
   runEventBusMethodWithParams( DISPATCH_EVENT_METHOD, getDispatchEventMethodParams(event));
}

The interesting thing here is the method parameter – a class object Event. The class itself Event described by two fields this type (event type) and detail (description of the event).

If class Event most likely you and I will have something in common, the class description Detail depends on the project, I have this:

public class Detail {
   private String status;
   private Integer currentStep;
   private Integer allSteps;
   private Boolean agree;
}

Oh, and what a method. getDispatchEventMethodParams(Event event) needed to collect the final line with the parameters of the eventBus method dispatchEvent.

Inside the method, depending on the event type, the event object from the template is collected:

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());
}

I used JavaScript files as a template, for example src/main/resources/eventBus/eventBusAgreeResultEventTemplate.js. An example of the contents of one of these files:

{
   agree : __DETAIL_AGREE_STATUS_VALUE__
}

The required value for DETAIL_AGREE_STATUS_VALUE substituted by simple substring replacement:

private String getAgreeEventDetailTemplate(Detail detail) {
   return readFile("eventBus/eventBusAgreeResultEventTemplate.js")
           .replace("__DETAIL_AGREE_STATUS_VALUE__", String.valueOf(detail.getAgree()));
}

I think we can come up with a more elegant solution) But it works and so far I’m happy with it.

How to verify that an event has been sent

I have the following problem – how to make sure that the method worked during the test. If the method in the console does not work correctly or does not work at all, then we will not know about the problem in time. And the next step of the test will show the problem; for example, it will not find some element that should have appeared after sending the event to EventBus and will fall. It's not entirely informative.

But there is a solution.

Event Bus does not store a history of all sent events, but only a list of the last sent events of a specific type.

That is, having received the object of the last event, we can check the type and even the object itself. As I understand it, since EventBus is a pattern, the developer himself decides what the implementation will be. I was lucky and had the ability to receive extreme events of certain event types through an object lastEventValues eventBus object. By the way, it was in order to receive the return value that we added “return” at the beginning of the input line of the method executeJavaScript.

It looks like this: window.crazy_game_event_bus.lastEventValues

An object lastEventValues is a map of objects, where one of the keys is a list of events, and the value is another map, where the keys are event types, and the values ​​are detail events. And it had to be parsed somehow. There is a class to solve this problem ru/example/eventBus/providers/EventBusObjectProvider.java.

One interesting thing in this class is the method getEventObjDetail:

private Detail getEventObjDetail(Map<String, Object> 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;
}

This method collects an object Detailso that the object can be compared later Detail from the previously sent event and object Detail obtained from the list of recent events. The disadvantage is that you need to describe each field of the object separately detailhow it's done here ru/example/eventBus/enums/DetailFieldEnum.java. But on the other hand, it is unlikely that this object will be voluminous.

This way we can verify that the event was sent successfully.

But again, if you don’t have this option, you can simply send an event or ask the developer to help you, I think it won’t be difficult for him. So this check is optional.

Conclusion

From sources describing EventBus, it becomes clear that the implementation of this pattern can vary greatly in different projects by different developers. But, I hope my implementation will help you understand the topic a little faster and figure out a solution to your problem. Naturally, I do not claim to be an expert in this topic and will be open to the comments and opinions of senior and experienced colleagues, autotesters and developers)) My solution is working, so I decided to share it, but your criticism will help me improve it!

I'm trying to blog https://t.me/qanva_blogcome in)

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *