Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions articles/building-apps/testing/browserless/_hello-world-view.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.`HelloWorldView.java`
[source,java]
----
@Route("")
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());
});
add(name, sayHello);
}
}
----
93 changes: 93 additions & 0 deletions articles/building-apps/testing/browserless/configure-tests.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: Configure a Browserless Test
page-title: Configure a Browserless Test | Vaadin
description: Set Vaadin properties and feature flags for individual browserless tests while retaining the correct framework setup.
meta-description: Set Vaadin properties and feature flags for individual browserless tests while retaining the correct framework setup.
order: 35
---


= [since:com.vaadin:vaadin@V25.3]#Configure a Browserless Test#

Use this guide after setting up a <<./#choose-your-framework,plain Java, Spring Boot, or Quarkus test>> with its standard environment initialization.
The Java EE/CDI guide replaces that initialization and does not automatically apply this configuration; retain its custom setup rather than copying these annotations into it.

== Configure One Test Class

Add `@BrowserlessTestConfig` to the existing test class to enable a feature and set a Vaadin deployment property.
For the plain Java setup:

[source,java]
----
@BrowserlessTestConfig(
applicationProperties = "devmode.sessionSerialization.enabled=true",
featureFlags = "defaultAutoResponsiveFormLayout")
class CartViewTest extends BrowserlessTest {
// Existing tests for CartView.
}
----

Keep `SpringBrowserlessTest` and `@SpringBootTest` for Spring Boot, or `QuarkusBrowserlessTest` and `@QuarkusTest` for Quarkus.
Choose an identifier from <<{articles}/flow/configuration/feature-flags#,Feature Flags>>; an unknown identifier fails instead of silently enabling a new feature.
Run the affected tests with `mvn test` and assert the application behavior under the selected setting.
The settings are local to the test environment, so the test does not need to edit a feature-flags file or reset system properties afterwards.

== Override a Setting for One Method

To exercise the same view with the feature disabled, add this annotation to the test method that verifies the fallback behavior:

[source,java]
----
@BrowserlessTestConfig(featureFlags = "defaultAutoResponsiveFormLayout=false")
----

The method-level flag replaces the class-level flag while retaining its application property.
Use a per-method environment for this pattern. `BrowserlessClassExtension` shares one environment and rejects method-level configuration.
See <<{articles}/flow/testing/browserless/test-configuration#merging-class-and-method-configuration,Configuration Merging>> for inheritance and precedence rules.

== Override a Spring-Defined Property

In a Spring Boot test, a Vaadin property already defined in the Spring environment takes precedence over `@BrowserlessTestConfig`.
Use Spring's `@TestPropertySource` on that test class to change it:

[source,java]
----
@SpringBootTest
@TestPropertySource(properties =
"vaadin.devmode.sessionSerialization.enabled=true")
class CartViewTest extends SpringBrowserlessTest {
// Existing tests for CartView.
}
----

This section is Spring-specific; it does not configure the Weld archive in the Java EE/CDI guide.

== Configure an Extension or Multiple Users

For a plain Java JUnit extension, configure the instance that creates the environment:

[source,java]
----
@RegisterExtension
BrowserlessExtension extension = new BrowserlessExtension()
.withFeatureFlags("defaultAutoResponsiveFormLayout");
----

For an application context created in a multi-user test, configure its builder:

[source,java]
----
try (var app = BrowserlessApplicationContext.create(builder -> builder
.withViewPackages(CartView.class)
.withFeatureFlags("defaultAutoResponsiveFormLayout"))) {
var window = app.newUser().newWindow();
window.navigate(CartView.class);
// Exercise the view and assert the behavior enabled by the flag.
}
----

An application context created this way does not read annotations on the test class.
See <<{articles}/flow/testing/browserless/test-configuration#configuring-without-annotations,Programmatic Configuration>> for explicitly importing a configuration, registering Lookup services, and combining programmatic settings with annotations.


[discussion-id]`C941CDE1-ACC5-47F3-82D9-90A3D1D0C1FC`
119 changes: 119 additions & 0 deletions articles/building-apps/testing/browserless/debug-tests.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: Debug a Failing Browserless Test
page-title: Debug a Failing Browserless Test | Vaadin
description: Enable failure snapshots, inspect component state, and fix a failing browserless interaction test.
meta-description: Enable failure snapshots, inspect component state, and fix a failing browserless interaction test.
order: 90
---


= Debug a Failing Browserless Test

Start with a configured test class from <<./#choose-your-framework,the guide for your application framework>>.
Keep its base class and setup when adding the snapshot annotation.
The example below uses a directly constructed view and the plain Java setup.

When a browserless test fails, it can be hard to tell why. The assertion message might say a component wasn't found or had an unexpected value, but it doesn't show you what the UI _actually_ looked like. UI snapshots solve this by printing a text representation of the entire component tree at the moment of failure, so you can see exactly what was on screen.


== Enabling Snapshots

Snapshots aren't enabled by default. Add the [annotationname]`@ExtendWith(TreeOnFailureExtension.class)` annotation to your test class:

[source,java]
----
@ExtendWith(TreeOnFailureExtension.class)
class HelloWorldViewTest extends BrowserlessTest {
...
}
----

For Java EE/CDI, add the same annotation to the existing CDI-aware test class:

[source,java]
----
@ExtendWith(TreeOnFailureExtension.class)
class CdiGreetingViewTest extends AbstractCdiViewTest {
// Keep the test methods from the CDI guide.
}
----

When any test in the class fails, the extension automatically prints the UI tree to the test output alongside the failure message.


See <<{articles}/flow/testing/browserless/snapshots#reading-a-snapshot,Snapshot Format>> for the properties represented in the output.


== Using Snapshots to Debug Failures

Suppose you have a view like this:

[source,java]
----
@Route("")
public class HelloWorldView extends HorizontalLayout {

TextField name;
Button sayHello;

public HelloWorldView() {
name = new TextField("Your name");
sayHello = new Button("Say hello");
sayHello.addClickListener(e -> {
if (!name.getValue().isEmpty()) {
Notification.show("Hello " + name.getValue());
}
});
add(name, sayHello);
}
}
----

And a test for it:

[source,java]
----
@Test
public void clickSayHello_showsGreeting() {
HelloWorldView view = navigate(HelloWorldView.class);
test(view.sayHello).click();
Notification notification = find(Notification.class).single();
assertEquals("Hello World", test(notification).getText());
}
----

The test fails because no [classname]`Notification` was found. The assertion error alone doesn't explain why. With snapshots enabled, the test output includes the UI tree:

----
└── UI[]
└── HelloWorldView[@theme='margin spacing']
├── TextField[label='Your name', value='']
└── Button[caption='Say hello']
----

Now you can see:

- There's no [classname]`Notification` in the tree -- the click didn't produce one.
- The [classname]`TextField` has `value=''` -- the name field is empty.
- Looking back at the view code, the click handler only opens the notification when the name is nonempty. The test forgot to set the name before clicking.

Set the name before clicking the button, then rerun the test:

[source,java]
----
test(view.name).setValue("World");
test(view.sayHello).click();
Notification notification = find(Notification.class).single();
Assertions.assertEquals("Hello World", test(notification).getText());
----


== Tips

- **Look for what's missing.** If a query like `find(Notification.class).single()` fails, the snapshot shows you that the component isn't there. Check the tree for clues about why it wasn't created.
- **Check property values.** When an assertion on a component's text or value fails, find that component in the tree and compare its actual properties to what you expected.
- **Watch for unexpected components.** If `find(Button.class).single()` fails because multiple buttons were found, the snapshot shows you all of them so you can narrow your query.
- **Inspect the layout hierarchy.** If a component appears in the tree but a scoped query like `findInView(TextField.class)` can't find it, the snapshot helps you see whether the component is nested inside the expected parent.


[discussion-id]`7B14417A-0C9B-4B46-8945-AC2564AD5F82`
57 changes: 57 additions & 0 deletions articles/building-apps/testing/browserless/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Browserless Testing
page-title: Browserless Testing | Vaadin
description: Choose the browserless test setup for Spring Boot, Java EE/CDI, Quarkus, or plain Java, then test your Flow views.
meta-description: Choose the browserless test setup for Spring Boot, Java EE/CDI, Quarkus, or plain Java, then test your Flow views.
order: 10
---


= Browserless Testing

Browserless tests exercise Flow views and components in the test JVM.
Choose the setup that matches your application's dependency injection framework before writing a test.

[#choose-your-framework]
== Choose Your Framework

[cols="1,2,2"]
|===
| Application | Setup Guide | Test Environment
| Spring Boot
| Start with [[first-browserless-test]][[navigating-to-a-view]][[using-the-java-api-directly]][[simulating-user-actions-with-testers]][[finding-components]][[running-tests]]<<setup-spring-boot#,Set Up Browserless Tests with Spring Boot>>
| `SpringBrowserlessTest` with `@SpringBootTest` and Spring-managed beans.
| Java EE / Jakarta EE with Vaadin CDI
| <<setup-cdi#,Set Up Browserless Tests with Java EE/CDI>>
| An application-owned `AbstractCdiViewTest` extending `BrowserlessTest`, with Weld and `CdiVaadinServlet`.
| Quarkus
| <<setup-quarkus#,Set Up Browserless Tests with Quarkus>>
| `QuarkusBrowserlessTest` with `@QuarkusTest`.
| Plain Java without a dependency injection container
| <<setup-without-spring#,Set Up Browserless Tests in Plain Java>>
| `BrowserlessTest` or a browserless JUnit extension.
|===

The CDI guide uses Jakarta packages (`jakarta.*`), as used by current Vaadin applications.
Quarkus has its own integration even though it also uses CDI concepts.

== Keep the Framework Setup with the Test

Use one setup path for each test class.
Keep that path's base class, dependencies, lifecycle hooks, and bean configuration when adapting an interaction example.

* Spring Boot tests use Spring configuration and Spring Security test annotations where applicable.
* Java EE/CDI tests use a Weld test archive, CDI producers, scopes, and alternatives. They extend the CDI-aware base class from the CDI guide. Adding Spring annotations or `browserless-test-spring` does not configure CDI.
* Quarkus tests use Quarkus test profiles and security annotations.
* Plain Java examples construct components without a dependency injection container. Extending `BrowserlessTest` alone does not enable CDI injection.

Methods such as `navigate()`, `find()`, and `test()` are shared interaction APIs.
A shared method does not make the surrounding framework setup interchangeable.
The task guides identify their example setup and explain which parts to keep when using CDI.

== Guides

section_outline::[]


[discussion-id]`E6858195-4B1D-43C8-8688-502531C6AC16`
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: Migrating from UI Unit Testing
page-title: How to migrate from UI Unit Testing to Browserless Testing | Vaadin
description: Steps for migrating existing tests from the old UI Unit Testing module to the new Browserless Testing module.
meta-description: Migrate Vaadin UI Unit Tests to Browserless Testing. Covers dependency changes, base class replacements, and TestBench coexistence.
order: 90
title: Migrate UI Unit Tests to Browserless Tests
page-title: Migrate UI Unit Tests to Browserless Tests | Vaadin
description: Update UI Unit Testing dependencies and base classes, handle Spring support, and migrate existing JUnit tests.
meta-description: Update UI Unit Testing dependencies and base classes, handle Spring support, and migrate existing JUnit tests.
order: 110
---


= Migrating from UI Unit Testing
= Migrate UI Unit Tests to Browserless Tests

Browserless testing replaces the older UI Unit Testing module. The testing API is the same; only the dependency and base class names have changed.

Expand Down Expand Up @@ -46,7 +46,7 @@ Replace your existing UI Unit Testing dependency with `browserless-test-junit6`.
</dependency>
----

For Quarkus-based projects, also add `browserless-test-quarkus`. See <<quarkus#, Browserless Testing in Quarkus-based Projects>> for details.
For Quarkus-based projects, also add `browserless-test-quarkus`. See <<setup-quarkus#,Set Up Browserless Tests with Quarkus>> for details.


[role="since:com.vaadin:vaadin@V25.2"]
Expand All @@ -70,6 +70,10 @@ No code changes are needed; class names and packages are unchanged. Non-Spring p

== Replace Base Classes

Select the base class matching your application framework.
Java EE/CDI tests need the custom initialization in <<setup-cdi#,the CDI setup>>; the plain `BrowserlessTest` replacement alone does not enable CDI.


Rename the test base classes as follows:

[cols="1,1,1"]
Expand All @@ -96,7 +100,7 @@ If you're migrating from JUnit 4 (`vaadin-testbench-unit`), you also need to upd

== TestBench End-to-End Coexistence

Browserless tests and TestBench end-to-end tests can coexist in the same project. The two modules use separate package namespaces, so having both `browserless-test-junit6` and `vaadin-testbench` on the test classpath doesn't cause conflicts.
Browserless tests and TestBench end-to-end tests can coexist in the same project. The two modules use separate Java packages, so having both `browserless-test-junit6` and `vaadin-testbench` on the test classpath doesn't cause conflicts.


== Keeping JUnit 4 Tests
Expand Down
Loading
Loading