From 32495dc671b3fbae190e407445acd87e960f072d Mon Sep 17 00:00:00 2001 From: Marco Collovati Date: Wed, 24 Jun 2026 04:50:16 +0000 Subject: [PATCH] refactor: reuse Vaadin environment for `PER_CLASS` Spring tests `SpringBrowserlessTest` rebuilt the Vaadin environment for every test method even under `@TestInstance(PER_CLASS)`, so `SpringBrowserlessPerClassLifecycleTest` could not prove it is created once. Reuse the auto-detecting `BrowserlessTestExtension` via `@ExtendWith` so the environment is created once per class when requested, and strengthen `SpringBrowserlessPerClassLifecycleTest` to assert the same `VaadinService`, `VaadinSession` and `UI` are reused across tests. --- .../browserless/SpringBrowserlessTest.java | 33 +++++++++---- ...pringBrowserlessPerClassLifecycleTest.java | 48 +++++++++++++++---- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/spring/src/main/java/com/vaadin/browserless/SpringBrowserlessTest.java b/spring/src/main/java/com/vaadin/browserless/SpringBrowserlessTest.java index 9826e302..9a2e0aa5 100644 --- a/spring/src/main/java/com/vaadin/browserless/SpringBrowserlessTest.java +++ b/spring/src/main/java/com/vaadin/browserless/SpringBrowserlessTest.java @@ -17,8 +17,6 @@ import java.util.Set; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -62,7 +60,7 @@ * } * */ -@ExtendWith({ SpringExtension.class }) +@ExtendWith({ SpringExtension.class, BrowserlessTestExtension.class }) @TestExecutionListeners(listeners = BrowserlessTestSpringLookupInitializer.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public abstract class SpringBrowserlessTest extends BaseBrowserlessTest implements TesterWrappers { @@ -82,13 +80,19 @@ protected Set> lookupServices() { } /** - * Sets up the mock Vaadin Spring environment before each test. Runs as a - * {@code @BeforeEach} method so that it fires after all JUnit 5 - * extension {@code beforeEach} callbacks — in particular after - * {@code SpringExtension.beforeEach()}, which populates the Spring Security - * context for annotations such as {@code @WithMockUser}. + * Sets up the mock Vaadin Spring environment. + *

+ * The lifecycle is driven by {@link BrowserlessTestExtension}: for the + * default per-method lifecycle it is invoked from the extension's + * {@code beforeEach} callback, and for {@code @TestInstance(PER_CLASS)} + * from {@code beforeAll}, so the environment is created once and reused + * across the test methods. + *

+ * {@link BrowserlessTestExtension} is declared after + * {@link SpringExtension} in {@code @ExtendWith} so that this init fires + * after {@code SpringExtension.beforeEach()}, which populates the Spring + * Security context for annotations such as {@code @WithMockUser}. */ - @BeforeEach @Override protected void initVaadinEnvironment() { scanTesters(); @@ -98,7 +102,16 @@ protected void initVaadinEnvironment() { initSignalsSupport(); } - @AfterEach + /** + * Tears down the mock Vaadin Spring environment. + *

+ * The lifecycle is driven by {@link BrowserlessTestExtension}: for the + * default per-method lifecycle it is invoked from the extension's + * {@code afterEach} callback, and for {@code @TestInstance(PER_CLASS)} from + * {@code afterAll}, so the environment created in + * {@link #initVaadinEnvironment()} is torn down once after all test + * methods. + */ @Override protected void cleanVaadinEnvironment() { super.cleanVaadinEnvironment(); diff --git a/spring/src/test/java/com/vaadin/browserless/SpringBrowserlessPerClassLifecycleTest.java b/spring/src/test/java/com/vaadin/browserless/SpringBrowserlessPerClassLifecycleTest.java index da4159fe..54281056 100644 --- a/spring/src/test/java/com/vaadin/browserless/SpringBrowserlessPerClassLifecycleTest.java +++ b/spring/src/test/java/com/vaadin/browserless/SpringBrowserlessPerClassLifecycleTest.java @@ -16,32 +16,60 @@ package com.vaadin.browserless; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; +import com.vaadin.flow.component.UI; import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinSession; +/** + * Verifies that {@link SpringBrowserlessTest} combined with + * {@code @TestInstance(PER_CLASS)} creates the Vaadin environment once and + * reuses the same {@link VaadinService}, {@link VaadinSession} and {@link UI} + * across all test methods in the class. + */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ContextConfiguration(classes = SpringBrowserlessPerClassLifecycleTest.TestConfig.class) class SpringBrowserlessPerClassLifecycleTest extends SpringBrowserlessTest { + private VaadinService sharedService; + private VaadinSession sharedSession; + private UI sharedUI; + + @BeforeAll + void captureVaadinEnvironment() { + sharedService = VaadinService.getCurrent(); + sharedSession = VaadinSession.getCurrent(); + sharedUI = UI.getCurrent(); + Assertions.assertNotNull(sharedService, + "VaadinService should be available after PER_CLASS init"); + Assertions.assertNotNull(sharedSession, + "VaadinSession should be available after PER_CLASS init"); + Assertions.assertNotNull(sharedUI, + "UI should be available after PER_CLASS init"); + } + @Test - void perClassLifecycle_firstTest_vaadinEnvironmentIsSetup() { - Assertions.assertNotNull(VaadinService.getCurrent(), - "VaadinService should be available with PER_CLASS lifecycle"); - Assertions.assertNotNull(VaadinSession.getCurrent(), - "VaadinSession should be available with PER_CLASS lifecycle"); + void firstTest_sameVaadinEnvironment() { + assertSameEnvironment(); } @Test - void perClassLifecycle_secondTest_vaadinEnvironmentIsSetup() { - Assertions.assertNotNull(VaadinService.getCurrent(), - "VaadinService should be available with PER_CLASS lifecycle"); - Assertions.assertNotNull(VaadinSession.getCurrent(), - "VaadinSession should be available with PER_CLASS lifecycle"); + void secondTest_sameVaadinEnvironment() { + assertSameEnvironment(); + } + + private void assertSameEnvironment() { + Assertions.assertSame(sharedService, VaadinService.getCurrent(), + "PER_CLASS lifecycle must reuse the same VaadinService across tests"); + Assertions.assertSame(sharedSession, VaadinSession.getCurrent(), + "PER_CLASS lifecycle must reuse the same VaadinSession across tests"); + Assertions.assertSame(sharedUI, UI.getCurrent(), + "PER_CLASS lifecycle must reuse the same UI across tests"); } @Configuration