Docs

Documentation versions (currently viewingVaadin 25.4 (pre-release))

Application, User, and Window Contexts

Browserless application, user, and window contexts — the main test API — with thread affinity, window activation, security isolation, factories, and configuration.

The BrowserlessApplicationContext API is the main way to write browserless tests. A test creates an application context, creates users from it, and opens windows for the users. Each window provides the testing methods — navigation, component queries, testers, and locators — and drives its own UI.

A typical test needs one user with one window:

Source code
Java
try (var app = BrowserlessApplicationContext.create(CartView.class)) {
    var window = app.newUser().newWindow();
    window.navigate(CartView.class);
    window.findButton().withText("Add to cart").click();
    // assertions...
}

The same API also covers tests that need several participants: application-wide state observed by two users, per-window UI state of the same user, or security context isolation when switching between authenticated users.

Contexts and Base Classes

The base classes — BrowserlessTest, SpringBrowserlessTest, and QuarkusBrowserlessTest — and the JUnit 6 extensions create one user with one window for each test method, or for each test class with BrowserlessClassExtension, and provide the same testing methods on the test class or the extension. They differ from an application context as follows:

  • A base class or an extension supports one user and one window. An application context creates as many as the test needs.

  • A base class reads its configuration from annotations, such as @ViewPackages, @ComponentTesterPackages, and @BrowserlessTestConfig. An application context takes its configuration from the code that creates it, and ignores those annotations.

  • A base class applies Spring Security and Quarkus test annotations, such as @WithMockUser and @TestSecurity, to its user. A secured application context gives each user the credentials passed to newUser().

  • A base class always recreates the environment before each test method. The test decides how long an application context lives.

The CDI integration extends a base class; the application context API doesn’t apply its custom setup.

Application, User, and Window

The API exposes three nested contexts that mirror Vaadin’s runtime hierarchy. The single-user Weld/CDI recipe does not automatically map these users to independent CDI session contexts.

The context types are:

BrowserlessApplicationContext

One VaadinService and one servlet, shared across every user and window the test creates.

BrowserlessUserContext

One VaadinSession and the security state of a single logical user. A user can own many windows.

BrowserlessUIContext

One UI instance — a single browser window. Provides the testing DSL (navigate, find, test, …​).

Note
All three contexts are thread-affine: within a single test, each context must be created, used, and closed on the same thread, and cannot be shared with other threads. Tests can still run in parallel — each test owns its own contexts.

When the test calls a DSL method on any BrowserlessUIContext, the API automatically switches the thread-local Vaadin state — VaadinService, VaadinSession, UI, request, response, and the security context — to that window’s user. Interleaving calls on different windows is therefore safe without any explicit context switch.

Application Context Lifecycle

The application context owns the users and windows it creates. It implements explicit resource cleanup through close(): closing the application context cascades to every user and window it created.

A test creates the context in a @BeforeEach method and closes it in an @AfterEach method, or creates it with try-with-resources in the test method itself. Creating it in a static @BeforeAll method shares it across the tests of a class; see Sharing the Vaadin Environment.

In Spring Boot, a test configuration can define the context as a bean, which every test that uses the same Spring application context shares, and which Spring closes together with the Spring application context. Each test then opens a user of its own, and closes it after the test; see Set Up Browserless Tests with Spring Boot. Closing a user ends its session and clears the Vaadin and security state from the thread.

create() accepts the packages that contain @Route-annotated views, either as package names or as classes whose packages should be scanned. Passing classes plays well with IDE refactoring and is the preferred form.

The SpringBrowserlessApplicationContext and QuarkusBrowserlessApplicationContext factories pre-wire the framework-specific servlet and lookup initialization. The Spring factory also accepts the Spring application context.

Creating Users and Windows

newUser() returns a fresh BrowserlessUserContext with its own VaadinSession. newWindow() creates a new UI for that user and navigates it to the root route, if one is registered. Different users have independent sessions; different windows of the same user share a session but have independent UI instances.

Every window exposes the testing DSL, scoped to that window — no explicit activation is required:

  • window.navigate(…​) — navigate the window to a view.

  • window.findButton(), window.findTextField(), window.findGrid(Class<V>), …​ — typed locator entry points that combine a filter chain with the component’s tester actions in one fluent expression; see Component Locators.

  • window.find(Class) and window.findInView(Class) — locate components in this window. Returns a ComponentQuery; see Querying Components.

  • window.test(component) — wrap a component in a tester that simulates user actions like click() and setValue(). Returns a typed tester matched to the component’s type; see Component Testers. To request a specific tester type explicitly — for example, a custom tester — use window.test(MyTester.class, component).

  • window.fireShortcut(Key, KeyModifier…​) — simulate a keyboard shortcut in the window.

  • window.runPendingSignalsTasks() — process pending signal effects before asserting; see Signals below.

  • window.getCurrentView() and window.roundTrip() — get the view the window shows, and simulate a server round trip.

Signals

The application context registers the test SignalEnvironment, so signal effects run deterministically instead of on a background thread pool. For signal testing in single-user tests — and a fuller treatment of synchronous propagation — see Testing Signals. When one window mutates a signal that other windows observe — the typical pattern for collaborative features built on shared signals — call runPendingSignalsTasks() to process the pending effects before asserting on the observing window.

See Signal Task Processing for waiting, return values, and session-lock handling.

The same call also confirms shared-signal writes made through the window: the SignalOperation returned by a write completes only after the queue has been drained. Write the signal while the window’s thread-locals are active — either from within a DSL call or after window.activate() — so that the confirmation is dispatched through the window’s UI. See Confirming a Write for details.

Authenticated Users with Spring Security

When Spring Security is on the classpath, SpringBrowserlessApplicationContext.createSecured() returns a SecuredBrowserlessApplicationContext<Authentication> that exposes credential-aware newUser(…​) overloads. The handler installs the user’s Authentication on the calling thread before SessionInit listeners fire, mirroring the order of operations in a real Vaadin + Spring Security request.

When the test switches between windows belonging to different users, the outgoing user’s SecurityContext is saved and the incoming user’s snapshot is restored automatically.

newUser(String username, String…​ roles) is a convenience that produces an Authentication with the conventions of @WithMockUser. To install a custom Authentication directly, pass it to newUser(Authentication). Calling newUser() without arguments creates an anonymous user; the handler installs Spring’s AnonymousAuthenticationToken. Spring Security test annotations, such as @WithMockUser, don’t affect the users of a secured context.

A logout performed in one window leaves the user logged out in all of that user’s windows, both already open and opened afterwards. To authenticate again, create a fresh user context with newUser(…​) rather than reusing the logged-out one.

Authenticated Users with Quarkus Security

The Quarkus factory follows the same pattern with SecurityIdentity as the credential type.

As with the Spring factory, newUser() without arguments creates an anonymous user, and cross-user window switches save and restore the active SecurityIdentity automatically.

Customizing the Application Context

For advanced setups, BrowserlessApplicationContext.Builder exposes the same configuration knobs as the framework-specific factories. Construct one directly and call build() for an unsecured context, or chain withSecurityContextHandler(…​) to transition to a typed SecuredBrowserlessApplicationContext.Builder<C> whose build() returns the credential-aware variant.

Table 1. Builder Configuration
Method Purpose

withViewPackages(String…​) / withViewPackages(Class<?>…​)

Adds packages to scan for @Route-annotated views. Successive calls accumulate.

withComponentTesterPackages(String…​) / withComponentTesterPackages(Class<?>…​)

Adds packages to scan for custom ComponentTester implementations.

withSecurityContextHandler(SecurityContextHandler<C>)

Enables credential-aware newUser(…​) overloads. Transitions to the typed SecuredBrowserlessApplicationContext.Builder<C>.

withServletFactory(…​)

Replaces the default mock servlet — for example, to plug in a framework-specific servlet.

withUIFactory(UIFactory)

Provides a custom UI subclass for every window created by the context.

withLookupServices(Class<?>…​)

Registers Vaadin Lookup service implementations.

withCloseHook(Runnable)

Registers a callback to run after the context tears down — intended for releasing framework-specific state.

withApplicationProperty(String, String) / withApplicationProperties(Map)

Sets Vaadin application properties for the environment the context creates.

withFeatureFlags(String…​) / withFeatureFlag(String, boolean)

Enables or disables feature flags for the environment the context creates. Both methods also have a Feature overload.

withConfiguration(BrowserlessConfiguration)

Applies a configuration built elsewhere as the baseline that the other methods add to, such as BrowserlessConfiguration.from(getClass()) to reuse what the test class declares with @BrowserlessTestConfig.

The property, feature flag, and configuration methods are the programmatic form of @BrowserlessTestConfig, which the context itself does not read; see Configuring without Annotations. SecuredBrowserlessApplicationContext.Builder exposes the same methods.

For one-off tweaks without holding on to a builder reference, BrowserlessApplicationContext.create(UnaryOperator<Builder>) and the corresponding createSecured(Function<Builder, SecuredBuilder<C>>) accept a configurer:

Source code
Java
try (var app = BrowserlessApplicationContext.create(b -> b
        .withViewPackages(CartView.class)
        .withCloseHook(() -> testFixtures.reset()))) {
    // ...
}

The Spring and Quarkus factories expose builder(…​) overloads that return a pre-wired builder for projects that need to add further customizations on top of the framework defaults.

Pitfalls and Guarantees

Common gotchas worth keeping in mind:

  • Always close the application context. Use try-with-resources or call close() in @AfterEach. Closing the application cascades to every user and window. Leaking a context across tests leaves Vaadin thread-locals pointing at torn-down state.

  • No parallel access. Every context is thread-affine. Driving the same BrowserlessApplicationContext from multiple threads in the same test is unsupported.

  • Security snapshot is per user, not per window. Two windows of the same user share one snapshot; a security mutation made while one window is active — including a logout — is visible to the user’s other windows, both already open and opened afterwards. The snapshot is re-captured only on cross-user switches.

  • Calling Vaadin APIs directly. DSL methods on BrowserlessUIContext activate the window automatically. If the test reaches for UI.getCurrent(), VaadinSession.getCurrent(), or SecurityContextHolder between DSL calls, call window.activate() first to make sure the thread-locals reflect the intended window. A window stays active after it’s created and after each DSL call, so in a test with a single window these APIs already reflect that window.

  • Spring request and session scopes follow the user. A @SessionScope or @RequestScope bean is resolved against the active user’s own request, so two users never share one instance. @VaadinSessionScope has always behaved this way, because it resolves against VaadinSession.getCurrent(). See Session-Scoped Beans for why such a bean can’t be autowired into a test class field. Resolving it from the test method instead gives the active user’s instance, so activate the intended user’s window first.

  • Anonymous users still go through the handler. On a secured context, newUser() with no arguments delegates to the handler, which installs its anonymous-equivalent state (for example, Spring’s AnonymousAuthenticationToken).

For worked examples, see the setup guides for a single user, and Test Multiple Users and Windows for several.

9C4E8A2D-6F31-4B72-9A8F-5D7E1C3B4F60