Writing unit test is an important part of development. An important part of unit testing is utilizing stubs and mocks in order to decouple various integrations and focus only on the logic in a particular method. Previously I went more in-depth on the topic in this article. Here in this development spike I demonstrate the basic concepts of stubbing and mocking objects in Unit tests.
You can find the source for this spike in SVN here
Environment Setup
Quick note on my project setup. I’m using Eclipse Helios with the m2Eclipse Maven plugin.
I use the JUnit and Mockito libraries in this example. If you want to include the jars manually you can get them here . Just download the jar and add it to your lib and classpath. I’m using maven and have already included the dependency in my pom.xml as
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
So in this example I’m using this idea that we have some order object that can calculate its total by iterating over items and applying appropriate tax. In this scenario the tax is pulled from a remote tax service, and I have some local implementation that’s managing all the connection logic separately.
The Object Under Test
Here is the method we’re going to test:
public double calculateOrderTotal() throws Exception { double subTotal = 0.0; for (int i=0; i
In order to manage this code better and make it more flexible I’m allowing the taxService to be set by adding a setter method. This can typically be added to most existing code without impacting the current functionality.
Existing code:
private TaxService taxService=new USTaxService();
Augmented with setter:
private TaxService taxService=new USTaxService(); public void setTaxService(TaxService taxService) { this.taxService=taxService; }
Testing with a Stub
One challenge with testing this calculate function is that we’re relying on the USTaxservice to be up d. What if it’s not functioning or we’re just working in an offline setup and can’t connect. Wouldn’t it be nice to be able to still test our code? This is where stubs come in.
In this example we setup and use a fake Stub object rather than a real USTaxService. Our stub doesn’t do anything, it just returns a predefined response.
TaxService taxService = mock(TaxService.class); stub(taxService.getTaxRate()).toReturn(.06); order.setTaxService(taxService);
First you see that we’re setting a local variable using a Mockito function “mock” to give us something that looks like a TaxService class.
Next we start to define how that stub should respond when it’s called. In this case we’re saying Stub out the getTaxRate function on our fake object. Whenever anyone calls it just return .06.
Finally we inject our fake taxService in the order Object.
When we exercise the test our code will use the stub and only exercise the logic in the method and not try to make any other system calls.
assertEquals(expectedResult, actualResult, acceptableDeviation);
Testing with Mocks
How can we be sure the code in the method actually behaved like it should? In many situations our methods may act on other object but never return anything. This is where mocks come in.
A mock is used to verify the behavior inside a method. For this example I want to verify that my method called the getTaxRate function on the tax service. Frequently developers might try to inspect the concrete TaxService itself but why not use a mock?
In this example we’ll create a mock object inject it into the Object under test, then we’ll ask that mock object if anyone ever called a specific method on it. If our Order object did what it was supposed to, our mock object will report back that yes, my getTaxRate function was called.
We create a mock object and inject it like we did previously:
TaxService taxService = mock(TaxService.class); stub(taxService.getTaxRate()).toReturn(.06); order.setTaxService(taxService);
Now we ask it if anyone called the getTaxRate Function:
verify(taxService).getTaxRate();
Mocks and stubs are a great way to decouple your integrations for testing. While the examples shown here are minimal in nature, the tools are very robust and able to suit most scenarios.
Here is the full code:
OrderTest.java
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import static org.mockito.Mockito.verify;
import org.junit.Test;
public class OrderTest {
@Test
public void testCalculateOrder() throws Exception{
// This is a typical test execution using a real service
// This test shows what our code would look like BEFORE using a stub
Order order = new Order();
TaxService taxService = new USTaxService();
order.setTaxService(taxService);
Item item = new Item();
item.setPrice(20.00);
order.addItem(item);
double expectedResult = 21.20;
double actualResult = order.calculateOrderTotal();
double acceptableDeviation = .01;
assertEquals(expectedResult, actualResult, acceptableDeviation);
}
@Test
public void testCalculateUsingMock() throws Exception{
Order order = new Order();
TaxService taxService = mock(TaxService.class);
stub(taxService.getTaxRate()).toReturn(.06);
order.setTaxService(taxService);
Item item = new Item();
item.setPrice(20.00);
order.addItem(item);
double expectedResult = 21.20;
double actualResult = order.calculateOrderTotal();
double acceptableDeviation = .01;
assertEquals(expectedResult, actualResult, acceptableDeviation);
verify(taxService).getTaxRate();
}
}
Order.java
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Order {
private TaxService taxService;
private List items = new ArrayList();
public void setTaxService(TaxService taxService) {
this.taxService=taxService;
}
public double calculateOrderTotal() throws Exception {
double subTotal = 0.0;
for (int i=0; i<items.size(); i++){
subTotal += items.get(i).getPrice();
}
subTotal += subTotal * taxService.getTaxRate();
return subTotal;
}
public void addItem(Item item) {
this.items.add(item);
}
}