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
UserListView
that publishes theUserListViewOpenedEvent
when attached to the UI. - A custom
TestUiEventPublisher
to track and store events during the test lifecycle. - A Spring test configuration to replace the default
UiEventPublisher
with the custom test publisher. - An integration test that navigates to the
UserListView
and 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
UserListView
class 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 theUserListViewOpenedEvent
is 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 theUserListViewOpenedEvent
for 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=true
in the test configuration. This allows your test configuration to override the standardUiEventPublisher
with theTestUiEventPublisher
. -
Test Event Publisher Configuration: Ensure that
TestUiEventPublisherConfiguration
is 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 injectTestUiEventPublisher
in 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