From 636a9666cae06e539fb4ab27ab223ab6fdb51f68 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 31 Mar 2026 06:26:18 +0000 Subject: [PATCH 1/2] fix: Fix TestSignalEnvironment to prevent flaky signal tests Use registerFirst() so TestSignalEnvironment takes priority over VaadinServiceEnvironment for effect dispatching. Return null from getResultNotifier() so result notifications run synchronously on the calling thread. Use deadline-based polling in runPendingTasks() to handle tasks that arrive after initial queue drain. Re-acquire the session lock before running each task so DOM operations work when effects run directly on the test thread. --- .../vaadin/testbench/unit/SignalsTest.java | 27 ++++++ .../testbench/unit/TestSignalEnvironment.java | 97 +++++++++++-------- 2 files changed, 85 insertions(+), 39 deletions(-) diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/SignalsTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/SignalsTest.java index 4683fddcb..246b8d312 100644 --- a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/SignalsTest.java +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/SignalsTest.java @@ -9,6 +9,7 @@ package com.vaadin.testbench.unit; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import com.example.base.signals.SignalsView; @@ -16,6 +17,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import com.vaadin.flow.signals.SignalEnvironment; + @ViewPackages(packages = "com.example.base.signals") @Timeout(10) public class SignalsTest extends UIUnitTest { @@ -67,6 +70,30 @@ void attachedComponent_triggerSignalFromNonUIThreadThroughComponentEffect_effect Assertions.assertEquals("Counter: 10", counterTester.getText()); } + @Test + void effectDispatcher_routesToTestQueue_notServiceThreadPool() + throws InterruptedException { + // On the test thread, both VaadinServiceEnvironment (from + // MockVaadin) and TestSignalEnvironment are active. The default + // effect dispatcher must resolve to TestSignalEnvironment's queue + // (via registerFirst) so that runPendingSignalsTasks() can drive + // effect execution deterministically. Without registerFirst, + // VaadinServiceEnvironment's thread pool would be used instead, + // making effects run asynchronously outside the test's control. + navigate(SignalsView.class); + + var latch = new CountDownLatch(1); + SignalEnvironment.getDefaultEffectDispatcher() + .execute(latch::countDown); + + Assertions.assertFalse(latch.await(50, TimeUnit.MILLISECONDS), + "Task should be queued, not executed immediately on a " + + "thread pool"); + runPendingSignalsTasks(); + Assertions.assertTrue(latch.await(0, TimeUnit.MILLISECONDS), + "Task should have executed after draining the test queue"); + } + @Test void attachedComponent_slowEffect_effectEvaluatedAsynchronously() { var view = navigate(SignalsView.class); diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TestSignalEnvironment.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TestSignalEnvironment.java index 960a63536..831723b9d 100644 --- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TestSignalEnvironment.java +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TestSignalEnvironment.java @@ -25,8 +25,10 @@ *

* How it works: *