如何使用@Autowired真实bean覆盖/替换在父类中声明的@MockBean字段

use*_*397 8 java integration-testing spring-test mockito spring-boot

为了避免 Spring 上下文一次又一次地重新加载,我已将带@MockBean注释的注入移至父类,如下所示。

@SpringBootTest
.......
public abstract  BaseTest {
    @MockBean
    protected OneService oneService;
Run Code Online (Sandbox Code Playgroud)

这为需要模拟的测试类提供服务OneService。但是,对于我也想从 扩展的测试BaseTest,但注入真实的OneServicevia@Autowired将不起作用,因为它将继承来自 的模拟注入BaseTest

public AnotherTest extends BaseTest {
    @Autowired
    protected OneService oneService;
Run Code Online (Sandbox Code Playgroud)

即使我使用了@Autowired注释,该oneService字段也将是从 继承的模拟实例BaseTest

有没有办法可以强制注入使用自动装配?

Sam*_*nen 6

在 Java 中,技术上没有办法覆盖字段。当您在子类中声明具有相同名称的字段时,该字段会隐藏超类中具有相同名称的字段。所以这是一个障碍。

第二个障碍源于这样一个事实:如果超类通过配置模拟,则 Spring Boot 的测试支持无法关闭子类中 bean 的模拟@MockBean

因此,解决方案是通过在超类中注入真正的 bean 来反转您尝试执行的操作@Autowired,然后在子类中打开对特定 bean 类型的模拟。

  1. 在超类中声明该@Autowired字段,但不要在子类中重新声明该字段。
  2. @MockBean在子类中的类级别声明,指定要模拟哪些类(即 bean 类型)。

最后一步会导致继承的字段被子类中的模拟注入。

以下两门课程在实践中演示了这种技术。

@SpringBootTest(classes = BaseTests.Config.class)
class BaseTests {

    @Autowired
    protected Service service;

    @Test
    void service() {
        assertThat(service.getMessage()).isEqualTo("real");
    }

    @Configuration
    static class Config {

        @Bean
        Service service() {
            return new Service();
        }
    }

    static class Service {
        String getMessage() {
            return "real";
        }
    }

}
Run Code Online (Sandbox Code Playgroud)
@MockBean(classes = BaseTests.Service.class)
class MockBeanTests extends BaseTests {

    @BeforeEach
    void setUpMock() {
        when(service.getMessage()).thenReturn("mock");
    }

    @Test
    @Override
    void service() {
        assertThat(service.getMessage()).isEqualTo("mock");
    }

}
Run Code Online (Sandbox Code Playgroud)