membersound
membersound

Reputation: 86757

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.

Upvotes: 6

Views: 3017

Answers (3)

membersound
membersound

Reputation: 86757

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.

Upvotes: 2

slauth
slauth

Reputation: 3178

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.

Upvotes: 11

SirHawrk
SirHawrk

Reputation: 758

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)

Upvotes: -1

Related Questions