If I was to define the dependency arrow in UML 3.0 I would draw it in red with a pitchfork. It is evil.
External dependencies like databases or webservices will slow down your team. The development environment will get complex with installations, configurations and debugging.
Mocking isn't the only answer. Working with values only, allow you to implement a slice of your application in isolation.
If you look at the example in why.groovy you might think it's strange. So, why you do it in your code?
void disposeOrders(Long userId) {
List<Order> orders = repository.findAllByUserId(userId);
for (Order order : orders) {
if(order.getTotal() > DISPOSE_THRESHOLD) {
repository.remove(order);
}
}
}
If the code above look familiar, be careful. In this code there is a mix of dependencies (repository.findAllByUserId
and repository.remove
) and rules (order.getTotal() > DISPOSE_THRESHOLD
) that may degrade maintainability.
To test this code you will need the real repository
(integration test) or mocks:
when(repository.findAllByUserId(1)).thenReturn(sample);
disposeOrders("1");
verify(repository).remove(sample.get(2));
verify(repository).remove(sample.get(7));
Basically it needs a lot of setup to test a single IF. This test might not look too bad at first, but in a larger scale with more dependencies it will end up being a mock festival.
To avoid this I'd suggest to separate dependencies and rules:
class DisposableOrders {
static Stream<Order> selectDisposable(List<Order> orders) {
return orders.stream().filter(order -> order.getTotal() > DISPOSE_THRESHOLD);
}
}
class OrderManager {
void disposeOrders(Long userId) {
selectDisposable(repository.findAllByUserId(userId)).forEach(order -> repository.remove(order));
}
}
Now the class DisposableOrders
has the rules and OrderManager
is responsible for integration. And the test should be simpler:
assertThat(selectDisposable(sampleOrders), contains(sample.get(2), sample.get(7)));
If you like you can write a integration test for OrderManager
or just don't cover it considering its simplicity.
One way or the other you will need to integrate your software and fight with availability and configuration of external resources. As much as you can defer it as better, so if you have the chance to implement you core slice with the correct separation of concerns go for it.