Dartz
Dartz

Reputation: 165

Java integration test with fake outbound call

I work on a Java project using Spring framework, JUnit and Mockito.

The application is in the middle of a chain with others application, so it exposes inbound ports (e.g. an HTTP API) to be called and uses outbound ports (e.g. web services and database) to call other apps.

I want to write something like an integration test that should pass through the whole java code from the inbound port to the outbound port, but without doing any call to anything that's outside of the project.

Let's take a very-simple-but-very-concrete example :

class diagram

We expose an HTTP endpoint to get customers and we call another app to get them.

Thus :

Now, I want to test my app by calling the CustomerControllerAdapter's getCustomers(), which will call the real service, which will call the real supplier, which will call a fake repository.

I wrote the following code :

@ExtendWith(SpringExtension.class)
class CustomerIntegrationTest {

    @Mock
    private CustomerRepository repository;

    @InjectMocks
    private CustomerControllerAdapter controller;

    @BeforeAll
    void setupAll() {
        CustomerOutboundPort customerOutboundPort = new CustomerSupplierAdapter(repository);
        CustomerInboundPort customerInboundPort = new CustomerService(customerOutboundPort);
        controller = new CustomerControllerAdapter(customerInboundPort);
    }

    @Test
    void bulkQuery() {
        // Given
        CustomerModel model = new CustomerModel();
        model.setName("Arya Stark");
        doReturn(List.of(model)).when(repository).getCustomers();

        // When
        List<CustomerDto> dtos = controller.getCustomers();

        // Then
        assertThat(dtos).hasSize(1);
        assertThat(dtos.get(0).getName()).isEqualTo("Arya Stark");
    }
    
}

But in this code, I do the "constructor's wiring" by myself in the setupAll() instead of relying on Spring dependency injection. It is not a viable solution because it would be very hard to maintain in real-life context (controller may have multiple services, service may have multiple suppliers, etc).

Actually, I would like to have something like an annotation to set on a CustomerRepository instance to programmatically overload dependency injection. Like : "Hey Spring, if any @Service class needs a CustomerRepository then you should use this fake one instead of the usual concrete implementation" without having to do the wiring by myself.

Is there any way to achieve that using Spring, JUnit, Mockito or anything else ?

Upvotes: 1

Views: 812

Answers (1)

Tom Cools
Tom Cools

Reputation: 1178

If you really want to replace every CustomerRepository in your tests (everywhere!) with a mock, I'd recommend going for a configuration which provides a @Bean, which creates a mocked bean.

@Profile("test")
@Configuration
public class TestConfiguration {
    @Bean
    @Primary
    public CustomerRepository customerRepostiory() {
        return Mockito.mock(CustomerRepository.class);
    }
}

@MockBean can have negative effects on your test duration as it's quite possible Spring needs to restart it's context.

Alternatively, I'd recommend NOT mocking your repository at all, but instead using either an in memory equivalent (H2) or the TestContainers framework to start the real database for you. Instead of mocking, you insert data into your repository before you start your tests.

Upvotes: 1

Related Questions