Take another look at the processAll()
method and its test, in listings 7.2 and 7.5. Most of what its test asserts is the interaction with the ports. Such assertions are easily done, and we did not need much more than basic Mockito. Now, let’s look closer at one specific assertion: verify(someCart).markAsReadyForDelivery(someDate);
. The someCart
instance of ShoppingCart
is not a mock but a spy. To ensure that the cart was marked as ready for delivery, we had to spy on the object. Mockito’s API enables us to spy objects with a single line of code. However, whenever we need a spy to assert the behavior, we must ask ourselves why we need a spy. Isn’t there an easier way?
In this particular case, we need to check whether ShoppingCart
is marked as ready for delivery after processing (listing 7.7). We can increase the observability of the ShoppingCart
class (in other words, we can make it simpler to observe the expected behavior of the shopping cart) by making it provide a method that indicates whether it is ready for delivery: isReadyForDelivery
.
Listing 7.7 Improving the observability of the ShoppingCart
class
public class ShoppingCart {
private boolean readyForDelivery = false;
// more info about the shopping cart...
public void markAsReadyForDelivery(Calendar estimatedDayOfDelivery) {
this.readyForDelivery = true;
// ...
}
public boolean isReadyForDelivery() { ❶
return readyForDelivery;
}
}
❶ The new isReadyForDelivery method is here to improve the observability of the class.
Because we can now easily ask ShoppingCart
whether it is ready for delivery, our test no longer requires a spy. A vanilla assertion should do. Here is the new test.
Listing 7.8 Avoiding the spy when testing PaidShoppingCartsBatch
@Test
void theWholeProcessHappens() {
PaidShoppingCartsBatch batch = new PaidShoppingCartsBatch(db,
➥ deliveryCenter, notifier, sap);
ShoppingCart someCart = new ShoppingCart(); ❶
assertThat(someCart.isReadyForDelivery()).isFalse();
Calendar someDate = Calendar.getInstance();
when(db.cartsPaidToday()).thenReturn(Arrays.asList(someCart));
when(deliveryCenter.deliver(someCart)).thenReturn(someDate);
batch.processAll();
verify(deliveryCenter).deliver(someCart);
verify(notifier).sendEstimatedDeliveryNotification(someCart);
verify(db).persist(someCart);
verify(sap).cartReadyForDelivery(someCart);
assertThat(someCart.isReadyForDelivery()).isTrue(); ❷
}
❶ No need for a spy anymore, as it is now easy to observe the behavior.
❷ Uses a simple vanilla assertion instead of a Mockito assertion
I urge you not to take this particular code change (the addition of a getter) as the solution for all observability issues. Rather, abstract away what we did here: we noticed that asserting that the shopping cart was marked as ready for delivery was not straightforward, as it required a spy. We then re-evaluated our code and looked for a simple way to let the test know that the shopping cart was marked as ready for delivery. In this case, a getter was the easy implementation.
Leave a Reply