5

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.

membersound
  • 81,582
  • 193
  • 585
  • 1,120

3 Answers3

10

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.

slauth
  • 2,667
  • 1
  • 10
  • 18
  • And then using `@Import(MockService1.class)` on my tests? That's indeed a nice idea, I always struggled with this because it's impossible to inject `@MockBean` in this case. But the `Mockito.mock()` seems to work! – membersound Jul 29 '21 at 11:24
  • @membersound yes, just `@Import` any mock you need in your test. – slauth Jul 29 '21 at 11:36
  • Alternatively, you can define configuration with `@SpringBootTest(classes = {MockService1.class})` – gindex Jul 29 '21 at 14:32
  • Any idea how to combine this with the extension (`@ExtendWith`) mechanism? The use case is that I want to some setup in the extension which also mocks (among other things) some methods on some beans. So I would like to ensure that by using that extension the mocked beans are also imported. – David Kubecka Jun 27 '23 at 13:26
1

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.

membersound
  • 81,582
  • 193
  • 585
  • 1,120
-1

I think your answer lies here: https://stackoverflow.com/a/50802303/10811865

Create a test profile that implements those MockBeans like so:

@Profile("test")
@Configuration
public class MyMockConfiguration {

    @Bean
    public SomeService someService() {
        SomeService someService = mock(SomeService .class);
        // mocked methods and results
        return someService ;
    }

And then use that profile with these annotations on your test class:

@ActiveProfiles("test")
@SpringBootTest
@WebAppConfiguration
@RunWith(SpringRunner.class)
SirHawrk
  • 610
  • 1
  • 4
  • 17
  • 1
    Well, but I would want to load the mocked beans selective! This would enable the mocks always for all tests using the "test" profile (or I would have to create N testprofiles - which is not good either), and is thus of no advantage over simple inheritance from an abstract testclass. – membersound Jul 29 '21 at 09:56
  • 1
    Ah my bad I misunderstood. You might wanna add to your question that you want to load the mocks independently of one another – SirHawrk Jul 29 '21 at 09:58