vaadin / vaadin/testbench

SpringUIUnitTest Parallel Execution Issue

Open
#1,990 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Java
Stars
25
Forks
25
Avg merge
5h 9m
Merged PRs (30d)
44

Description

SpringUIUnitTest Parallel Execution Issue

Summary

SpringUIUnitTest fails when test classes are executed in parallel due to bean registration conflicts in the shared Spring application context.

Environment

  • Vaadin: 24.8.7
  • Spring Boot: 3.5.5
  • JUnit 5: 5.11.3
  • Java: 21

Problem Description

When running multiple test classes that extend SpringUIUnitTest in parallel, tests fail with:

java.lang.IllegalStateException: Could not register object [com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer@XXX] 
under bean name 'com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer': 
there is already object [com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer@YYY] bound

Root Cause

The UITestSpringLookupInitializer class registers beans as singletons in the Spring context. When multiple test classes run in parallel and share the same Spring context, they attempt to register the same bean multiple times, causing conflicts.

Minimal Reproducible Example

https://github.com/Artur-/springuitest-parallel-issue

Expected Behavior

Tests should run successfully in parallel without bean registration conflicts.

Actual Behavior

Tests fail with:

java.lang.IllegalStateException: Could not register object [com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer@1d76957d] 
under bean name 'com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer': 
there is already object [com.vaadin.testbench.unit.mocks.SpringSecurityRequestCustomizer@27fa67ee] bound
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.addSingleton(DefaultSingletonBeanRegistry.java:162)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.addSingleton(DefaultListableBeanFactory.java:1460)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.registerSingleton(DefaultSingletonBeanRegistry.java:146)
    at com.vaadin.testbench.unit.UITestSpringLookupInitializer.beforeTestMethod(UITestSpringLookupInitializer.java:56)

Analysis

The issue occurs in UITestSpringLookupInitializer.beforeTestMethod() which registers beans without checking if they already exist:

// Problematic code in UITestSpringLookupInitializer
@Override
public void beforeTestMethod(TestContext testContext) {
    ConfigurableApplicationContext context = (ConfigurableApplicationContext) testContext.getApplicationContext();
    ConfigurableListableBeanFactory factory = context.getBeanFactory();
    
    // This fails when multiple threads try to register the same bean
    factory.registerSingleton(SpringSecurityRequestCustomizer.class.getName(), 
                             new SpringSecurityRequestCustomizer());
    // ... other bean registrations
}

Workarounds

Workaround 1: Disable Parallel Execution
# junit-platform.properties
junit.jupiter.execution.parallel.enabled=false
Workaround 2: Use @DirtiesContext (Slow)
@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public abstract class BaseUITest extends SpringUIUnitTest {
    // Each test class gets its own context, but this is very slow
}
Workaround 3: Use @Isolated Annotation
@Isolated // Forces sequential execution for this test class
public class CriticalUITest extends BaseUITest {
    // Tests run sequentially
}

Suggested Fix

The UITestSpringLookupInitializer should check if beans are already registered before attempting to register them:

@Override
public void beforeTestMethod(TestContext testContext) {
    ConfigurableApplicationContext context = (ConfigurableApplicationContext) testContext.getApplicationContext();
    ConfigurableListableBeanFactory factory = context.getBeanFactory();
    
    String beanName = SpringSecurityRequestCustomizer.class.getName();
    
    // Check if bean is already registered
    if (!factory.containsSingleton(beanName)) {
        synchronized (factory) {
            // Double-check inside synchronized block
            if (!factory.containsSingleton(beanName)) {
                factory.registerSingleton(beanName, new SpringSecurityRequestCustomizer());
            }
        }
    }
    
    // ... similar pattern for other beans
}

Or alternatively, use prototype scope or unique bean names per test:

String beanName = SpringSecurityRequestCustomizer.class.getName() + "_" + testContext.getTestClass().getName();
factory.registerSingleton(beanName, new SpringSecurityRequestCustomizer());

Impact

  • Performance: Forces users to run tests sequentially, significantly increasing test execution time
  • CI/CD: Longer build times in continuous integration pipelines
  • Developer Experience: Slower feedback loop during development
  • Scalability: Cannot leverage multi-core processors for test execution

Additional Notes

  • This issue affects all projects using SpringUIUnitTest with parallel test execution
  • The problem becomes more pronounced as the test suite grows
  • Modern CI environments encourage parallel execution for faster builds

References

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with UITestSpringLookupInitializer.beforeTestMethod() and the linked minimal reproduction, then run the tests with JUnit parallel execution enabled to observe the singleton registration conflict. Trace the other bean registrations in this initializer and verify that the test suite completes in parallel without duplicate-bean failures.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
backend, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.