Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create reusable @MockBean definitions in @SpringBootTest?

I have a @SpringBootTest class that has a rather complex mock definition setup with mocked return values.

Question: can I externalize @MockBean setups into an own class, so I could reuse the mock configuration in multiple class (sidenote: I'm not looking for inheritance here!).

@SpringBootTest
public class ServiceTest extends DefaultTest {
    @Autowired
    private ServiceController controller;
    
    @MockBean
    private Service1 s1;
    
    @MockBean
    private Service2 s2;
    
    @MockBean
    private Service3 s3;
    
    //assume more complex mock definitions
    @BeforeEach
    public void mock() {
        when(s1.invoke()).thenReturn(result1);
        when(s2.invoke()).thenReturn(result2);
        when(s3.invoke()).thenReturn(result3);
    }
    
    @Test
    public void test() {
        //...
    }
}

I want to load the mocks independently of each other, not globally for all my tests.

like image 208
membersound Avatar asked Apr 12 '21 07:04

membersound


People also ask

What is the difference between @mock and @MockBean?

We can use the @MockBean to add mock objects to the Spring application context. The mock will replace any existing bean of the same type in the application context. If no bean of the same type is defined, a new one will be added.

What is @MockBean in Junit?

@MockBean: ( org.springframework.boot.test.mock.mockito.MockBean ) Annotation that can be used to add mocks to a Spring ApplicationContext. Can be used as a class level annotation or on fields in either @Configuration classes, or test classes that are @RunWith the SpringRunner.

What is MockBean annotation?

The @MockBean is a Spring Boot test annotation that is used to add mocks to ApplicationContext . 2. A mock will replace existing bean of the same type defined in the context and if no existing bean then new one will be added to context. 3. The @MockBean can be used at field level and class level in unit test classes.

How do you use spring Mockbeans?

@MockBean annotation It can be used as a class level annotation or on fields in either @Configuration classes, or test classes that are @RunWith the SpringRunner. Any existing single bean of the same type defined in the context will be replaced by the mock. If no existing bean is defined a new one will be added.


2 Answers

Not directly what you are asking for, but one possibility is to not use @MockBean but instead define your reusable mocks as @Primary @Beans in multiple @TestConfigurations that you can selectively @Import in your tests:

@TestConfiguration
public class MockService1 {

  @Bean
  @Primary
  public Service1 service1Mock() {
    Service1 s1 = Mockito.mock(Service1.class);
    when(s1.invoke()).thenReturn("result1");
    return s1;
  }
}

There's a nice article about this approach: Building Reusable Mock Modules with Spring Boot.

like image 150
slauth Avatar answered Nov 15 '22 05:11

slauth


Just for reference (if the @TestConfiguration approach might not be suitable for whatever reason), it also works when creating a junit Extension:

public class MockService1Extension implements BeforeTestExecutionCallback {
    @Override
    public void beforeTestExecution(ExtensionContext extensionContext) throws Exception {
        ApplicationContext springContext = SpringExtension.getApplicationContext(extensionContext);
        Service1 s1 = springContext.getBean(Service1.class);
        when(s1.invoke()).thenReturn("result1");
    }
}


@ExtendWith(MockService1Extension1.class)
@MockBean({Service1.class})
public class TestImpl {
    @Test
    public void test() {
    }
}

Unfortunately, for this approach, the implementing test must list the beans mocked inside the extension additionally with @MockBean on class level.

like image 32
membersound Avatar answered Nov 15 '22 04:11

membersound