Docs

Documentation versions (currently viewingVaadin 24)

Getting Started with UI Unit Testing

Tutorial to create and run a UI unit test.

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>

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.

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 the ComponentTester 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 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