How to Test UI Event Publishing in Jmix – A Community Solution
Recently, a question came up on Slack about how to test if specific UI events are published in a Jmix application. To address this, I’ve built a small example demonstrating how to implement such a test.
You can find the complete example project on GitHub:
GitHub Repository: Jmix UI Integration Test Events
Why Would You Test UI Event Publishing?
In Jmix applications, custom UI events are often used to trigger or handle actions in response to user interaction. For example, you might want to log when a user opens a certain view or fire events when certain conditions are met. It’s essential to verify that these events are published correctly, especially in larger applications with complex UI workflows.
With this test setup, you can check if your custom UI events are published as expected when certain actions, such as navigating to a view, take place.
Example Overview
In the example, we perform an integration test for a custom event, UserListViewOpenedEvent, which is fired when the UserListView is opened. The test captures the published event and verifies that it has been triggered.
The key parts of this project include:
- A production
UserListViewthat publishes theUserListViewOpenedEventwhen attached to the UI. - A custom
TestUiEventPublisherto track and store events during the test lifecycle. - A Spring test configuration to replace the default
UiEventPublisherwith the custom test publisher. - An integration test that navigates to the
UserListViewand validates that the event is published.
Full Example
Here’s the relevant content from the project’s README.md:
Jmix Integration Test: Custom Event Publishing
This example project demonstrates how to perform integration tests in a Jmix application that involve publishing custom UI events. Specifically, it shows how to capture and validate that a custom event, UserListViewOpenedEvent, is published when a specific view (UserListView) is opened.
Overview
The project uses the Jmix framework with Spring Boot and Vaadin for UI. We extend Jmix’s UiEventPublisher to track published events during test execution. This is done using a custom test utility TestUiEventPublisher.
The project includes:
- A production
UserListViewclass that publishes a custom event when the view is attached. - A custom event publisher (
TestUiEventPublisher) that records all UI events during test execution. - A test configuration class to register this custom publisher.
- An integration test (
UserUiTest) that verifies if theUserListViewOpenedEventis published when the user navigates to theUserListView.
Key Classes
1. UserListView
This is the production class that defines the user list view. It uses Vaadin’s @Route to define the URL path and publishes a UserListViewOpenedEvent when the view is attached to the UI.
@Route(value = "users", layout = MainView.class)
@ViewController("User.list")
@ViewDescriptor("user-list-view.xml")
@LookupComponent("usersDataGrid")
@DialogMode(width = "64em")
public class UserListView extends StandardListView<User> {
@Autowired
private UiEventPublisher uiEventPublisher;
@Subscribe
public void onAttachEvent(final AttachEvent event) {
uiEventPublisher.publishEventForCurrentUI(new UserListViewOpenedEvent(this));
}
}
-
Key method:
onAttachEvent(AttachEvent event)triggers when the view is attached to the UI and publishes theUserListViewOpenedEventfor the current UI.
2. TestUiEventPublisher
This class extends Jmix’s UiEventPublisher to capture all published events during testing. It overrides the publish method to store each event in a list for later verification.
public class TestUiEventPublisher extends UiEventPublisher {
private final List<ApplicationEvent> publishedEvents = new ArrayList<>();
public TestUiEventPublisher(SystemAuthenticator systemAuthenticator, ClusterApplicationEventPublisher clusterApplicationEventPublisher, SessionHolder sessionHolder, CurrentAuthentication currentAuthentication) {
super(systemAuthenticator, clusterApplicationEventPublisher, sessionHolder, currentAuthentication);
}
public List<ApplicationEvent> getPublishedEvents() {
return publishedEvents;
}
@Override
protected void publish(Collection<UI> uis, ApplicationEvent event) {
super.publish(uis, event);
publishedEvents.add(event);
}
}
-
Key method:
getPublishedEvents()returns the list of events published during the test.
3. TestUiEventPublisherConfiguration
A Spring test configuration that overrides the default UiEventPublisher with TestUiEventPublisher for test purposes.
@TestConfiguration
public class TestUiEventPublisherConfiguration {
@Bean("flowui_UiEventPublisher")
public UiEventPublisher uiEventPublisher(SystemAuthenticator systemAuthenticator, ClusterApplicationEventPublisher clusterApplicationEventPublisher, SessionHolder sessionHolder, CurrentAuthentication currentAuthentication) {
return new TestUiEventPublisher(systemAuthenticator, clusterApplicationEventPublisher, sessionHolder, currentAuthentication);
}
}
4. UserUiTest
An integration test that simulates the navigation to the UserListView and verifies that the UserListViewOpenedEvent is fired.
Important Test Configuration Notes:
-
Bean Definition Override: It’s important to set the property
spring.main.allow-bean-definition-overriding=truein the test configuration. This allows your test configuration to override the standardUiEventPublisherwith theTestUiEventPublisher. -
Test Event Publisher Configuration: Ensure that
TestUiEventPublisherConfigurationis included in the test classes configuration. This replaces the default publisher with the test-specific version, allowing event tracking during tests. -
Inject the
TestUiEventPublisher: Instead of injecting the regularUiEventPublisher, make sure to injectTestUiEventPublisherin the test class to gain access to thegetPublishedEvents()method, which is crucial for validating that events have been published.
@UiTest
@SpringBootTest(
properties = {"spring.main.allow-bean-definition-overriding=true"},
classes = {
JmixSampleUiIntegrationTestEventsApplication.class,
FlowuiTestAssistConfiguration.class,
TestUiEventPublisherConfiguration.class // Ensure Test configuration is included
})
public class UserUiTest {
@Autowired
DataManager dataManager;
@Autowired
ViewNavigators viewNavigators;
@Autowired
private TestUiEventPublisher testUiEventPublisher; // Inject the test event publisher
@Test
void when_openListView_then_UserListViewOpenedEventFired() {
// when:
viewNavigators.view(UiTestUtils.getCurrentView(), UserListView.class).navigate();
// then:
assertThat(testUiEventPublisher.getPublishedEvents()) // Use TestUiEventPublisher
.hasSize(1);
// and:
assertThat(testUiEventPublisher.getPublishedEvents())
.anyMatch(applicationEvent -> applicationEvent instanceof UserListViewOpenedEvent);
}
@AfterEach
void tearDown() {
dataManager.load(User.class)
.query("e.username like ?1", "test-user-%")
.list()
.forEach(u -> dataManager.remove(u));
}
}
Running the Tests
To run the integration tests, use the following command:
./gradlew test