Speed Up Browserless Tests
- Measure First
- Restrict Package Scanning
- Keep Copilot Disabled in Spring Boot Tests
- Use a Reduced Spring Application Context
- Share the Vaadin Environment Across Tests
Browserless tests scan the classpath for routes and error views and, in Spring Boot projects, load the full application context. For large projects this can slow down test startup. The following techniques help reduce bootstrap time. With Java EE/CDI, the setup already registers routes and beans explicitly; keep its Weld archive and route registrations small instead.
Measure First
Measure the run time of the suite before you change anything, and again after each change. Keep only the changes that give a useful improvement.
Restrict Package Scanning
An application context scans only the packages of the classes passed to create(), and their sub-packages.
Pass a class from each package that contains views the test needs, rather than a class from a top-level package that contains the whole application.
Classes keep the scan focused and safe to refactor:
Source code
Java
app = BrowserlessApplicationContext.create(MyView.class, MainLayout.class);The base classes scan the whole classpath by default.
Restrict them with the @ViewPackages annotation, such as @ViewPackages(classes = MyView.class) on the test class.
See Route Scanning for all forms.
Keep Copilot Disabled in Spring Boot Tests
Copilot works on top of an application running in a browser, so it has little use in a browserless test, and initializing it only adds to the test’s setup. Browserless tests therefore disable Copilot by default, and there is nothing to configure.
The exception is a Spring Boot application that sets vaadin.copilot.enable in its application.properties.
That value takes precedence over the browserless default, so if it enables Copilot, Copilot initializes in every test as well.
Override the property in the tests:
Source code
Java
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
@SpringBootTest
@TestPropertySource(properties = "vaadin.copilot.enable=false")
class MyViewTest {
// Autowire the application context as in the setup guide.
}To apply the override to every test, put the annotation on a shared base class that the test classes extend. See Copilot for how to enable Copilot in a test.
Use a Reduced Spring Application Context
Instead of @SpringBootTest, which loads the full application context, you can annotate the test with @SpringJUnitConfig to provide only the beans needed for the test. This is useful when you want to replace real services with test doubles.
For a view that injects a service, register a test implementation with the same interface. This example uses a service-backed variant of the greeting view:
Source code
Java
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
public interface GreetingService {
String greet(String name);
}
@Route("service-greeting")
public class ServiceGreetingView extends HorizontalLayout {
final TextField name = new TextField("Your name");
final Button sayHello = new Button("Say hello");
public ServiceGreetingView(GreetingService greetings) {
sayHello.addClickListener(event ->
Notification.show(greetings.greet(name.getValue())));
add(name, sayHello);
}
}Place each public type in its own file. Put the test in the view’s package:
Source code
Java
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.vaadin.browserless.BrowserlessApplicationContext;
import com.vaadin.browserless.BrowserlessUIContext;
import com.vaadin.browserless.SpringBrowserlessApplicationContext;
@SpringJUnitConfig(GreetingTestConfig.class)
class ServiceGreetingViewTest {
@Autowired
private ApplicationContext applicationContext;
private BrowserlessApplicationContext app;
private BrowserlessUIContext window;
@BeforeEach
void setUp() {
app = SpringBrowserlessApplicationContext.create(applicationContext,
ServiceGreetingView.class);
window = app.newUser().newWindow();
}
@AfterEach
void tearDown() {
app.close();
}
@Test
void greeting_usesTestService() {
window.navigate(ServiceGreetingView.class);
window.findTextField().withLabel("Your name").setValue("Test");
window.findButton().withText("Say hello").click();
Assertions.assertEquals("Hello Test",
window.findNotification().getText());
}
}
@TestConfiguration
class GreetingTestConfig {
@Bean
GreetingService greetingService() {
return name -> "Hello " + name;
}
}@SpringJUnitConfig runs the test with Spring’s JUnit extension and loads only the given configuration, so the Spring application context contains the test service but not the rest of the application.
It also doesn’t component-scan the shared browserless application context from the setup guide, so the test creates one of its own for each test, scanning only the package of the tested view.
A test that extends SpringBrowserlessTest, which registers Spring’s JUnit extension itself, selects the configuration with @ContextConfiguration(classes = GreetingTestConfig.class) instead.
Annotate the configuration with @TestConfiguration rather than @Configuration.
Spring Boot’s component scan skips test configuration classes, so the test service doesn’t also end up in the @SpringBootTest contexts of other tests in the same package, where it would conflict with the application’s service.
|
Note
|
Prefer replacing services this way — a Bean overrides create separate cached Spring contexts and can affect authentication in browserless test suites. See Application Context Isolation for the integration behavior. For service-level tests that don’t need the Vaadin environment, test the service on its own: construct it with stub collaborators instead of overriding a bean in a Spring context. |
Share the Vaadin Environment Across Tests
By default, each test creates its own application context, user, and window — the session, the UI, and the views it navigates to — and closes them afterwards. For classes with many tests that navigate to views sharing the same MainLayout, this setup cost can dominate the test runtime.
To reuse a single Vaadin environment across all test methods in a class, create the application context and the window in a static @BeforeAll method, and close the context in an @AfterAll method.
Every test then uses the same UI instance:
Source code
Shared Environment Example
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.vaadin.browserless.BrowserlessApplicationContext;
import com.vaadin.browserless.BrowserlessUIContext;
class HelloWorldViewSharedTest {
private static BrowserlessApplicationContext app;
private static BrowserlessUIContext window;
@BeforeAll
static void setUp() {
app = BrowserlessApplicationContext.create(HelloWorldView.class);
window = app.newUser().newWindow();
window.navigate(HelloWorldView.class);
}
@AfterAll
static void tearDown() {
app.close();
}
@Test
void name_isInitiallyEmpty() {
Assertions.assertEquals("", window.findTextField()
.withLabel("Your name").component().getValue());
}
@Test
void greetingButton_isEnabled() {
Assertions.assertTrue(window.findButton()
.withText("Say hello").component().isEnabled());
}
}These read-only tests use the HelloWorldView from the plain Java setup guide.
They can run in either order because neither changes the shared UI.
To share less, keep the application context in the static field, but create a new user and window in a @BeforeEach method, and close the user with window.getUser().close() in an @AfterEach method.
Each test then gets a fresh session and UI, while the tests still share the Vaadin service.
In Spring Boot, the tests already share the application context bean from the setup guide, and each test opens and closes its own user.
To share the window too, open it in a static @BeforeAll method that gets the bean as a parameter, such as static void openWindow(@Autowired BrowserlessApplicationContext app), and don’t close the user.
|
Warning
| With a shared environment, state leaks between tests. Tests that mutate state must reset it explicitly. Re-navigating can reset view-local state, but does not reset session or application-owned data. Prefer a shared environment for read-only or independent interactions; stick with the default per-method lifecycle when tests mutate shared state in conflicting ways. |
The application context is bound to the thread that creates it, so this works with JUnit’s default sequential execution, which runs @BeforeAll, the tests, and @AfterAll of a class on the same thread.
|
Note
|
The base test classes — BrowserlessTest, SpringBrowserlessTest, and QuarkusBrowserlessTest — always reinitialize the Vaadin environment before each test method. Annotating them with @TestInstance(PER_CLASS) therefore does not share the Vaadin environment, although it can still be useful for sharing other per-class state. In a plain Java test without an application context, a static BrowserlessClassExtension shares the environment instead; see JUnit 6 Extensions.
|
A3B7E2F1-5D89-4C6A-9E12-7F4A8B3C6D50