Getting Started with UI Unit Testing
Setting Up Your Project
To start creating UI unit tests in an existing project, you need to add the TestBench UI Unit Testing
dependency (com.vaadin
/vaadin-testbench-unit
) with a test
scope. Assuming you have imported the Vaadin
Bill-of-Materials (BOM) and have a Maven project, all you need to do is add the following:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-testbench-unit</artifactId>
<scope>test</scope>
</dependency>
Creating Your First UI Unit Test
To start unit testing your Vaadin UIs, create a class that extends UIUnitTest
(for JUnit 5) or UIUnit4Test
(for JUnit 4).
The base class instantiates a UI along with all the necessary Vaadin environment, which is available to your test methods.
Tip
|
Testing with Spring
This guide shows how to implement UI unit tests in plain Java projects.
For information about testing with Spring or Spring Boot, consult the UI Unit Testing with Spring.
|
class HelloWorldViewTest extends UIUnitTest {
@Test
public void setText_clickButton_notificationIsShown() {
final HelloWorldView helloView = navigate(HelloWorldView.class);
// TextField and Button are available as package protected in the view
// So we can use those from there
test(helloView.name).setValue("Test");
test(helloView.sayHello).click();
// Notification isn't referenced in the view so we need to use the component
// query API to find the notification that opened
Notification notification = $(Notification.class).first();
Assertions.assertEquals("Hello Test", test(notification).getText());
}
}
By default, the base class scans whole classpath for routes and error views, but this behavior can be changed for a faster bootstrap.
To restrict the scan to specific packages and their sub-packages, annotate the test class with ViewPackages
and specify the package by filling the classes()
array with classes that are members of the desired packages, or by providing the packages with fully qualified names in the packages()
property.
Using classes()
is the preferred way, since it plays well with IDEs refactoring when moving classes to different packages.
@ViewPackages(classes={ MyView.class, OtherView.class })
class MyViewTest extends UIUnitTest {
}
@ViewPackages(packages={ "com.example.app.pgk1", "com.example.app.pgk2" })
class MyViewTest extends UIUnitTest {
}
@ViewPackages(
classes={ MyView.class, OtherView.class },
packages={ "com.example.app.pgk1", "com.example.app.pgk2" }
)
class MyViewTest extends UIUnitTest {
}
Using the annotation without providing classes()
or packages()
acts as a shortcut for restricting the scan to the current test class package and sub-packages.
@ViewPackages // same as @ViewPackages(classes=MyViewTest.class)
class MyViewTest extends UIUnitTest {
}
Running Tests
Testing with UIUnitTest
does don’t require any particular setup to be executed.
Run the test directly from your IDE or use Maven, for example by typing mvn test
on the terminal.
Navigating in Unit Tests
On test initialization, the loaded view is the root view.
To navigate to another registered view, the UIUnitTest
base class contains navigate()
methods that support navigation to different supported views.
-
For a normal view with only a path defined
navigate(MyView.class)
navigate("myView", MyView.class)
-
For a view with
HasUrlParameter
navigate(MyParam.class, "parameter")
navigate("myParam/parameter", MyParam.class)
-
For a view with URL template
@Route("template/:param")
navigate(Template.class, Collections.singletonMap("param", PARAMETER))
navigate("template/myParam", Template.class)
All navigation methods return the instantiated view, so that the package private fields can be used directly from the view for testing.
Note
| Navigation by location string takes in the view class, so that the initialized view can be automatically validated to be the expected one. |
// Navigate to InputView
InputView input = navigate(InputView.class);
// Get the nameField TextField from InputView and then get the Tester to operate on it
TextFieldTester nameField_ = test(input.nameField);
// use the tester to set the value, to do required checks and fire expected events
nameField_.setValue("User input");
// Assert in another component that the change event fired and it has the correct value
Assertions.assertEquals("User input", input.changeText.getText());
Testing Components
The aim of browser-less testing isn’t to test the components as is, but to simulate user actions and data "seen" on the client side.
To help with actions and getting data, there are testers for components that have methods for use with components.
In a UIUnitTest
class, you can get a tester for a component with test(component)
or test(Tester.class, component)
.
-
test(component)
returns a component-specific tester, if one can be determined for the given component, or theComponentTester
generic tester. -
test(Tester.class, component)
always returns an instance of the given tester.
For each method call, where it’s applicable, the tester methods check that the component is in a state where it could be used by the user. This means that the component should be visible, enabled, attached to the UI, and not behind a modal component.
Note
|
Only server modality is checked
The modality check only works when the modal component is server-side modal, as client modality isn’t defined on the server.
|
Sample test of the HelloWorld view.
@Route(value = "", layout = MainLayout.class)
public class HelloWorldView extends HorizontalLayout {
TextField name;
Button sayHello;
public HelloWorldView() {
name = new TextField("Your name");
sayHello = new Button("Say hello");
sayHello.addClickListener(e -> {
Notification.show("Hello " + name.getValue());
});
setMargin(true);
setVerticalComponentAlignment(Alignment.END, name, sayHello);
add(name, sayHello);
}
}
Note
|
The components are package-protected, so that we can use them directly in the UIUnitTest .
|
class HelloWorldViewTest extends UIUnitTest {
@Test
public void setText_clickButton_notificationIsShown() {
final HelloWorldView helloView = navigate(HelloWorldView.class);
// TextField and Button are available as package protected in the view
// So we can use those from there
test(helloView.name).setValue("Test");
test(helloView.sayHello).click();
// Notification isn't referenced in the view so we need to use the component
// query API to find the notification that opened
Notification notification = $(Notification.class).first();
Assertions.assertEquals("Hello Test", test(notification).getText());
}
}
7F423DA0-1C41-44BA-B832-55C269FA9311