spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.37k stars 38.04k forks source link

`@MockitoBean` can no longer replace a manually registered singleton #33678

Closed wilkinsona closed 19 hours ago

wilkinsona commented 20 hours ago

Affects: 6.2.0-SNAPSHOT

This is a regression since 6.2.0-RC1. The problem can be reproduced with the following:

package com.example;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import com.example.ManuallyRegisteredSingletonMockitoBeanTests.SingletonRegistrar;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(initializers = SingletonRegistrar.class)
class ManuallyRegisteredSingletonMockitoBeanTests {

    @MockitoBean
    SomethingToMock mock;

    @Test
    void test() {

    }

    static class SingletonRegistrar implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.getBeanFactory().registerSingleton("somethingToMock", new SomethingToMock());
        }

    }

    static class SomethingToMock {

    }   

}

It fails with 6.2.0-20241009.135715-934 (the latest 6.2.0 snapshot at the time of writing). It passes with 6.2.0-RC1. Unfortunately, this is preventing us from moving Boot's build to Framework 6.2.0 snapshots as I've been unable to find a workaround and the affected test cannot be easily disabled as it's used to produce Actuator API documentation.

jhoeller commented 20 hours ago

@sbrannen it looks like we need to call something equivalent to validateBeanDefinition (including a destroySingleton call) for containsSingleton=true as well, just like we do for containsBeanDefinition=true.

sbrannen commented 19 hours ago

I think a simple if (beanFactory.containsBeanDefinition(beanName)) check might suffice.

And if that's the case, https://github.com/spring-projects/spring-framework/commit/98bee41630059b566178ce18d047f24df7b8fa11 caused the regression.

sbrannen commented 19 hours ago

@wilkinsona, thanks for reporting this and especially for providing the reproducer!

I've added a modified version of that to our test suite and fixed the regression in https://github.com/spring-projects/spring-framework/commit/8d652e9c1265fc2cbfe26e99c4251c3748d3ff72.

Since this regression was introduced between releases, I've changed the label to task.

Please let us know if the change helps address the issues in Spring Boot's test suite.