Docs

JUnit 6 Extensions

Use BrowserlessExtension and BrowserlessClassExtension as an alternative to extending BrowserlessTest.

Extending BrowserlessTest is the most compact way to write browserless tests, but it requires your test class to use inheritance for the Vaadin setup. When a project already has its own test base class, or you prefer composition over inheritance, the BrowserlessExtension and BrowserlessClassExtension JUnit 6 extensions provide the same functionality without requiring a specific superclass.

Note
These extensions are part of the browserless-test-junit6 artifact. They don’t replace SpringBrowserlessTest or QuarkusBrowserlessTest — for Spring and Quarkus projects, continue to extend those base classes.

When to Use an Extension

Use an extension when any of the following applies:

  • The test class already extends another base class that can’t be changed.

  • The project standardizes on composition-based JUnit 6 extensions.

Per-Method Lifecycle

BrowserlessExtension creates a fresh Vaadin environment before each test method and tears it down after. Register it as an instance field with @RegisterExtension:

Source code
Java
@ViewPackages(classes = CartView.class)
class CartViewTest {

    @RegisterExtension
    BrowserlessExtension ext = new BrowserlessExtension();

    @Test
    void addItemToCart() {
        CartView view = ext.navigate(CartView.class);
        ext.test(view.getAddButton()).click();

        Assertions.assertEquals(1, view.getCartSize());
    }
}

Navigation, queries, and tester interactions are available as methods on the extension instance — ext.navigate(), ext.find(), ext.findInView(), ext.test(), ext.getCurrentView(), ext.fireShortcut(), ext.roundTrip(), and ext.runPendingSignalsTasks().

Per-Class Lifecycle

BrowserlessClassExtension initializes the Vaadin environment once in @BeforeAll and shares it across all tests in the class. Register it as a static field:

Source code
Java
@ViewPackages(classes = CartView.class)
class CartViewSharedTest {

    @RegisterExtension
    static BrowserlessClassExtension ext = new BrowserlessClassExtension();

    @BeforeAll
    static void setup() {
        ext.navigate(CartView.class);
    }

    @Test
    void addItem() {
        // same UI instance as removeItem
        ext.test(ext.find(Button.class).withText("Add").single()).click();
    }

    @Test
    void removeItem() {
        // state from addItem is preserved
    }
}

The same trade-offs as per-class lifecycle with BrowserlessTest apply: state leaks between tests, so write tests that tolerate leftover state or reset it explicitly.

Configuring the Extension

Both extensions support a builder-style API for configuration, used as an alternative or in addition to annotations.

Table 1. Builder API
Method Description

withViewPackages(Class<?>…​)

Adds the packages of the given classes to the route scan. Equivalent to @ViewPackages(classes = …​).

withViewPackages(String…​)

Adds package names (as strings) to the route scan. Equivalent to @ViewPackages(packages = …​).

withServices(Class<?>…​)

Registers custom implementations with the Vaadin Lookup SPI.

withComponentTesterPackages(String…​)

Adds packages to scan for custom ComponentTester implementations. Equivalent to @ComponentTesterPackages.

The @ViewPackages annotation still works when placed on the test class; programmatic configuration adds to what the annotation declares.

Source code
Extension with Programmatic Configuration
class AdminViewTest {

    @RegisterExtension
    BrowserlessExtension ext = new BrowserlessExtension()
            .withViewPackages("com.example.views", "com.example.admin")
            .withServices(CustomInstantiatorFactory.class)
            .withComponentTesterPackages("com.example.testers");

    @Test
    void adminDashboardLoads() {
        AdminDashboardView view = ext.navigate(AdminDashboardView.class);
        Assertions.assertNotNull(view);
    }
}

B51F9D4A-2E73-4B18-8C6F-9A3D7E2B1C04

Updated