4

I have a base test scenario that will be used by other integration tests. This scenario includes some mock beans (@MockBean) for external integrations.

Today, I have something like this in the integration test class:

@SpringBootTest
@WebAppConfiguration
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderIT {

And the fields and annotations to prepare my integration test:

private MockMvc mockMvc;

@Autowired
private WebApplicationContext wac;

@Autowired
private ObjectMapper mapper;

@MockBean
private SomeGateway someGateway;

@MockBean
private SomeRabbitMqService someRabbitMqService ;

@MockBean
private AnotherRabbitMqService anotherRabbitMqService;

@MockBean
private SomeIntegrationService someIntegrationService ;

@MockBean
private Clock clock;

@Before
public void setup() {
    //some methods mocking each service above, preparing mockMvc, etc
}

This scenario is necessary for use the MockMvc and create the main feature in the system, my Order. This Order is created by calling a POST method in a Rest API, saving the order in a memory database.

Even this working well, I need to duplicate this block of code containing these @MockBean and some @Autowired in another tests, because the Order is the base scenario to add Products to the order, set an Address to deliver, etc. Each scenario has a different integration test but all of them needs an Order.

So, how to share the "MockBeans" and the methods that mocks them among my Integration Tests? I had really bad experiences using inheritance among the tests and I really would like to try a different approach.

Dherik
  • 17,757
  • 11
  • 115
  • 164
  • "So, how to share the "MockBeans" and the methods that mock them among my Integration Tests? " You don't have thousand solutions to share some fields: inheritance or composition. The first one is less verbose of course. – davidxxx Mar 06 '18 at 20:55
  • What's the purpose of this test? Seems every dependent component is being mocked and the purpose of an integration test is precisely test the interaction between dependent services. – ootero Mar 07 '18 at 03:29
  • @ootero Call an API Rest and save/update/delete/get the `Order` (and related entities), saving in a database memory (H2 and Embedded MongoDB). There are a lot of other services (the order and related services) being tested in this process. I'm only mocking the external dependencies (events from/to RabbitMQ and WebService, basically), but there are some annotation setup (mapper, mockMvc, wac) that need to be repeated only to do a Rest Call on Spring and that bothers me too. – Dherik Mar 07 '18 at 11:38

2 Answers2

7

I end up using the Spring profiles.

I created a configuration class annotated with @Profile("test") and created the mocked beans there. Like:

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

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

And in the Test class:

@ActiveProfiles("test")
@SpringBootTest
@WebAppConfiguration
@RunWith(SpringRunner.class)
public class MyControllerIT {

If some integration test needs to override the current mock implementation on the profile, the test just needs to declare @MockBean on the class and proceed on the mock as usual.

I'm not decide yet if test is a good name, because for me makes more sense mock the configuration by "context". So, instead of use the generic name test on the profile name, I could use createOrder and have different configuration profiles, each one with a different name and different mocks: createOrder, createOrderWithoutProducts.

Dherik
  • 17,757
  • 11
  • 115
  • 164
2

I believe @ContextConfiguration was created for this purpose: https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles

jones-chris
  • 629
  • 2
  • 13
  • 29