在应用程序启动之前配置@MockBean组件

Mar*_*cel 22 java spring integration-testing mocking spring-boot

我有一个Spring Boot 1.4.2应用程序.在启动期间使用的一些代码如下所示:

@Component 
class SystemTypeDetector{
    public enum SystemType{ TYPE_A, TYPE_B, TYPE_C }
    public SystemType getSystemType(){ return ... }
}

@Component 
public class SomeOtherComponent{
    @Autowired 
    private SystemTypeDetector systemTypeDetector;
    @PostConstruct 
    public void startup(){
        switch(systemTypeDetector.getSystemType()){   // <-- NPE here in test
        case TYPE_A: ...
        case TYPE_B: ...
        case TYPE_C: ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有一个组件可以确定系统类型.在从其他组件启动期间使用此组件.在生产中一切正常.

现在我想使用Spring 1.4的@MockBean添加一些集成测试.

测试看起来像这样:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
    @MockBean 
    private SystemTypeDetector systemTypeDetectorMock;

    @Before 
    public void initMock(){
       Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
    }

    @Test 
    public void testNrOne(){
      // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上嘲弄工作正常.我使用了systemTypeDetectorMock,如果我调用@MockBean- > getSystemType返回.

问题是应用程序无法启动.目前弹簧工作顺序似乎是:

  1. 创建所有Mocks(没有配置所有方法返回null)
  2. 开始申请
  3. 调用@ Before-methods(将配置模拟的地方)
  4. 开始测试

我的问题是应用程序以未初始化的模拟开始.所以调用TYPE_C返回null.

我的问题是:如何应用程序启动之前配置模拟?

编辑:如果有人有同样的问题,一个解决方法是使用getSystemType().这称为真实组件,在我的情况下,系统启动.启动后我可以改变模拟行为.

Mac*_*iak 30

在这种情况下,您需要以我们在@MockBean引入之前习惯的方式配置模拟- 通过手动指定一个@Primarybean,该bean 将替换上下文中的原始 bean。

@SpringBootTest
class DemoApplicationTests {

    @TestConfiguration
    public static class TestConfig {

        @Bean
        @Primary
        public SystemTypeDetector mockSystemTypeDetector() {
            SystemTypeDetector std = mock(SystemTypeDetector.class);
            when(std.getSystemType()).thenReturn(TYPE_C);
            return std;
        }

    }

    @Autowired
    private SystemTypeDetector systemTypeDetector;

    @Test
    void contextLoads() {
        assertThat(systemTypeDetector.getSystemType()).isEqualTo(TYPE_C);
    }
}
Run Code Online (Sandbox Code Playgroud)

由于@TestConfigurationclass 是一个静态内部类,因此它只会被这个测试自动选择。您将放入的完整模拟行为@Before必须移至初始化 bean 的方法。

  • 设置 `spring.main.allow-bean-definition-overriding=true` 后它就起作用了 (3认同)

lan*_*ava 16

我能够像这样修复它

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
    // this inner class must be static!
    @TestConfiguration
    public static class EarlyConfiguration {
       @MockBean 
       private SystemTypeDetector systemTypeDetectorMock;

       @PostConstruct 
       public void initMock(){
          Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
       }
    }

    // here we can inject the bean created by EarlyConfiguration
    @Autowired 
    private SystemTypeDetector systemTypeDetectorMock;

    @Autowired
    private SomeOtherComponent someOtherComponent;

    @Test 
    public void testNrOne(){
       someOtherComponent.doStuff();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ahm*_*yed -2

我认为这是由于您自动装配依赖项的方式所致。看看这个(特别是关于“修复#1:解决您的设计并使您的依赖关系可见”的部分)。这样你也可以避免使用 ,@PostConstruct而只使用构造函数。