Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configure @MockBean component before application start

I have a Spring Boot 1.4.2 application. Some code which is used during startup looks like this:

@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: ...         }     } } 

There is a component which determines the system type. This component is used during startup from other components. In production everything works fine.

Now I want to add some integration tests using Spring 1.4's @MockBean.

The test looks like this:

@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(){       // ...     } } 

Basically the mocking works fine. My systemTypeDetectorMock is used and if I call getSystemType -> TYPE_C is returned.

The problem is that the application doesn't start. Currently springs working order seems to be:

  1. create all Mocks (without configuration all methods return null)
  2. start application
  3. call @Before-methods (where the mocks would be configured)
  4. start test

My problem is that the application starts with an uninitialized mock. So the call to getSystemType() returns null.

My question is: How can I configure the mocks before application startup?

Edit: If somebody has the same problem, one workaround is to use @MockBean(answer = CALLS_REAL_METHODS). This calls the real component and in my case the system starts up. After startup I can change the mock behavior.

like image 322
Marcel Avatar asked Nov 12 '16 13:11

Marcel


2 Answers

In this case you need to configure mocks in a way we used to do it before @MockBean was introduced - by specifying manually a @Primary bean that will replace the original one in the context.

@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);     } } 

Since @TestConfiguration class is a static inner class it will be picked automatically only by this test. Complete mock behaviour that you would put into @Before has to be moved to method that initialises a bean.

like image 185
Maciej Walkowiak Avatar answered Sep 19 '22 20:09

Maciej Walkowiak


I was able to fix it like this

@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();     } } 
like image 29
lance-java Avatar answered Sep 20 '22 20:09

lance-java