> Markdown version of [Application, User, and Window Contexts](https://vaadin.com/docs/next/flow/testing/browserless/multi-user). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Application, User, and Window Contexts (since V25.2)

`BrowserlessTest` and the JUnit 6 extensions cover the most common scenario: one user, one window. Some features can only be exercised with multiple participants in the same test — application-wide singletons observed by two users, per-window UI state of the same user, or security context isolation when switching between authenticated users.

The `BrowserlessApplicationContext` API models this directly. It is a composition-based, lower-level alternative that lets the test create as many user sessions and UI instances as needed, then drive them independently.

## <a id="when-to-use-this-api"></a>When to Use This API

Reach for `BrowserlessApplicationContext` when:

- Two or more users must be active at the same time — for example, asserting that a shared service exposes the same state to both, or that one user’s mutation does not leak into another user’s session.

- A single user has multiple browser windows open and per-window UI state must remain isolated while session state is shared.

- A test switches between authenticated users and needs the Spring Security or Quarkus security context to follow the active window.

For tests with a single user and a single window, prefer [`SpringBrowserlessTest`](https://vaadin.com/docs/next/building-apps/testing/browserless/setup-spring-boot.md), [`BrowserlessTest`](https://vaadin.com/docs/next/building-apps/testing/browserless/setup-without-spring.md), [`QuarkusBrowserlessTest`](https://vaadin.com/docs/next/flow/testing/browserless/quarkus.md), or the [JUnit 6 extensions](https://vaadin.com/docs/next/flow/testing/browserless/extensions.md).

## <a id="application-user-and-window"></a>Application, User, and Window

The API exposes three nested contexts that mirror Vaadin’s runtime hierarchy. The [single-user Weld/CDI recipe](https://vaadin.com/docs/next/flow/testing/browserless/cdi.md) 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.

## <a id="application-context-lifecycle"></a>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.

`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.

## <a id="creating-users-and-windows"></a>Creating Users and Windows

`newUser()` returns a fresh `BrowserlessUserContext` with its own `VaadinSession`. `newWindow()` creates a new `UI` for that user. Different users have independent sessions; different windows of the same user share a session but have independent `UI` instances.

Every window exposes the same testing DSL as `BrowserlessTest`, 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](https://vaadin.com/docs/next/flow/testing/browserless/locators.md).

- `window.find(Class)` and `window.findInView(Class)` — locate components in this window. Returns a `ComponentQuery`; see [Querying Components](https://vaadin.com/docs/next/flow/testing/browserless/component-query.md).

- `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](https://vaadin.com/docs/next/flow/testing/browserless/component-testers.md). 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, mirroring `fireShortcut()` on `BrowserlessTest`.

- `window.runPendingSignalsTasks()` — process pending signal effects before asserting; see [Signals](#signals) below.

- `window.getCurrentView()` and `window.roundTrip()` — convenience accessors that mirror their `BrowserlessTest` counterparts.

## <a id="signals"></a>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](https://vaadin.com/docs/next/flow/testing/browserless/testing-signals.md). 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](https://vaadin.com/docs/next/flow/testing/browserless/testing-signals.md#background-threads) 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](https://vaadin.com/docs/next/flow/testing/browserless/testing-signals.md#shared-signal-writes) for details.

## <a id="authenticated-users-with-spring-security"></a>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`.

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.

## <a id="authenticated-users-with-quarkus-security"></a>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.

## <a id="customizing-the-application-context"></a>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)` (since undefined) | Sets Vaadin application properties for the environment the context creates.                                                                                                                                     |
| `withFeatureFlags(String…​)` / `withFeatureFlag(String, boolean)` (since undefined)            | Enables or disables feature flags for the environment the context creates. Both methods also have a `Feature` overload.                                                                                         |
| `withConfiguration(BrowserlessConfiguration)` (since undefined)                                | 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](https://vaadin.com/docs/next/flow/testing/browserless/test-configuration.md#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:

```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.

## <a id="pitfalls-and-guarantees"></a>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.

- **Spring request and session scopes follow the user.** (since V25.2) 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](https://vaadin.com/docs/next/flow/testing/browserless/environment-differences.md#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 a worked example, see [Test Multiple Users and Windows](https://vaadin.com/docs/next/building-apps/testing/browserless/test-multiple-users.md).

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