In the previous article in this series, we’ve identified a number of common challenges related to dependencies in test environments, and seen how they can prevent development teams from testing in an efficient manner. We have seen that these challenges fall into one of two categories:
- Those related to being able to isolate a component from its dependencies (this is especially important for testing at the unit and component level).
- Those related to being able to incorporate dependencies in tests (important when testing at the integration and end-to-end level).
In this article, we’ll be looking at mocking classes in unit testing, a common strategy to deal with issues in the first category (isolation challenges).
Tutorial Chapters
- Increasing Test Efficiency with Simulations & Service Virtualization (Chapter 1)
- You’re here → Isolating Components in Unit Tests with Mockito (Chapter 2)
- Getting Started with Service Virtualization using WireMock (Chapter 3)
- Creating More Sophisticated Simulations with WireMock (Chapter 4)
Consider the situation where you’re building a web shop, and your code base includes a class Order containing, among other things, a method checkout() that, no surprises here, finalizes the order and performs payment processing:
public class Order { public Order() { } public String checkout(PaymentProvider paymentProvider) { try { return paymentProvider.processPayment().equalsIgnoreCase("Approved") ? "Success" : "Failure"; } catch(Exception e) { return "Exception occurred at payment provider when trying to checkout"; } } }
Depending on the result of the processPayment() method of the PaymentProvider class (and potentially other things too, but I’ve deliberately left those out), the checkout() method returns a result equal to either Success or Failure.
This means that if we want to write tests for the checkout() method in the Order class, we need to also create an instance of the PaymentProvider class. However, with unit tests, we typically want to test the smallest possible pieces of our code. That is, we don’t want to have to rely on the behaviour of the PaymentProvider class when we’re writing tests for the Order class.
That’s where mocking frameworks come into play. These frameworks allow you to create mocks of the classes that the method or class you want to test depend on, and, more importantly, they allow you to specify exactly how this mock version of your dependency should behave. By doing so, mocking allows you to isolate the behaviour of the method or class you want to test from any unwanted side effects caused by your dependencies (since you control their behaviour yourself).
In this post, I’ll show you some of the most important features of Mockito, a popular Java mocking framework. Note that most commonly used mocking frameworks (not just for Java but in general) offer similar features, so you should be able to apply what you see here with other mocking frameworks in other languages without too much trouble.
Let’s start with writing a test that checks that an order can be successfully checked out when the payment processing is successful. If we would use the actual implementation of the PaymentProvider class to do so, the test would look like this:
@Test public void checkoutOrder_usingRealDependency_shouldYieldSuccess() { Order order = new Order(); PaymentProvider pp = new PaymentProvider(); Assert.assertEquals( "Success", order.checkout(pp) ); }
There’s a problem here, however: the result of our test depends on the return value of the processPayment() method in the PaymentProvider class.
And when you look at the implementation of this method (see the code base in the link at the end of this article), you can see how it can cause troubles when you’re trying to write reliable unit tests for the Order class. To deal with these troubles, we’ll use Mockito to create a mock version on the PaymentProvider class, and tell it how to behave before we run our test:
@Test public void checkoutOrder_paymentProcessingSucceeds_shouldYieldSuccess() { Order order = new Order(); PaymentProvider ppMock = mock(PaymentProvider.class); when(ppMock.processPayment()).thenReturn("Approved"); Assert.assertEquals( "Success", order.checkout(ppMock) ); }
The Mockito magic happens in this line of code:
when(ppMock.processPayment()).thenReturn("Approved");
Here, we tell that our mock class, when its processPayment() method is invoked, should always return Approved, since that’s the behaviour we need in order to successfully test that our order checkout can be completed. By doing so, we remove the dependency on the behaviour of the actual processPayment() method.
But what if we also want to test that our order checkout fails when the payment processing is not successful? We can do that by configuring the mock to behave differently:
@Test public void checkoutOrder_paymentProcessingFails_shouldYieldFailure() { Order order = new Order(); PaymentProvider ppMock = mock(PaymentProvider.class); when(ppMock.processPayment()).thenReturn("Denied"); Assert.assertEquals( "Failure", order.checkout(ppMock) ); }
As you can see, we’ve explicitly defined the behaviour of your dependencies using the Mockito mocking framework.
How about testing whether exceptions thrown by dependencies are handled appropriately in our class under test? Under specific circumstances, our implementation of the processPayment() method throws an IllegalStateException. Mockito offers the thenThrow() option to configure our mock to do the same:
@Test public void checkoutOrder_paymentProviderThrowsException_shouldBeCaught() { Order order = new Order(); PaymentProvider ppMock = mock(PaymentProvider.class); when(ppMock.processPayment()).thenThrow(new IllegalStateException()); Assert.assertEquals( "Exception occurred at payment provider when trying to checkout", order.checkout(ppMock) ); }
Finally, in some cases it might also be valuable to check whether a class or method under test actually calls the correct dependency methods, and that it does so the expected amount of times, with the expected argument values. Mockito enables you to do this with the verify() method, as you can see in this example:
@Test public void checkoutOrder_verifyNumberOfCallsToProcessPayment_shouldBeOne() { Order order = new Order(); PaymentProvider ppMock = mock(PaymentProvider.class); when(ppMock.processPayment()).thenReturn("Approved"); order.checkout(ppMock); verify(ppMock, times(1)).processPayment(); }
Most mocking frameworks like Mockito offer many more features for powerful object mocking in unit tests. It is not the goal of this article to cover all of these features, but rather to give a solid introduction into what mocking is, what it looks like and how you can use it to your advantage when isolating your test object from its dependencies. For an extensive overview of Mockito features, please visit this page.
All the code samples from this article (and the upcoming articles in this series) can be found on this GitHub page: https://github.com/basdijkstra/testproject-mockito-wiremock.
See you in the next article! 😎