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 :
We expose an HTTP endpoint to get customers and we call another app to get them.
- In the domain : customers are represented by the
Customer
class. - In the externalapp layer : customers are represented by the
CustomerModel
class. - In the rest layer : customers are represented by the
CustomerDto
class.
Thus :
- The
CustomerSupplierAdapter
class gets data fromCustomerRepository
and does the mapping fromCustomerModel
toCustomer
. - The
CustomerControllerAdapter
class does the mapping fromCustomer
toCustomerDto
and returns the data.
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 ?