Java

Navigation

Recent site activity

Mockito examples

Mockito is a mock object framework for Java.  You can find it here.  The following example shows how to mock up the dependencies for your system under test or SUT.  In this particular example, I'm testing a PricingServiceImpl class and it has a dependency to a DataAccess type.  Using interface-based programming makes it easy to swap in different implementations of a supertype.  I'm a firm believer in dependency injection (DI).  Using DI makes mocking even easier when driving out a design with unit tests.  First we have the interface types for PricingService and DataAccess:

 1 package examples.mockito;
2
3
import java.math.BigDecimal;
4
5
/**
6 * @author Christopher Bartling, Pintail Consulting LLC
7 * @since Aug 31, 2008 1:18:11 AM
8 */
9 public interface PricingService {
10
11
void setDataAccess(DataAccess dataAccess);
12
13
BigDecimal getPrice(String sku) throws SkuNotFoundException;
14 }
 1 package examples.mockito;
2
3
import java.math.BigDecimal;
4
5
/**
6 * @author Christopher Bartling, Pintail Consulting LLC
7 * @since Aug 31, 2008 1:18:22 AM
8 */
9 public interface DataAccess {
10
11 BigDecimal getPriceBySku(String sku);
12 }



Next up is the PricingServiceImpl class, which is the system under test or SUT for the subsequent unit test. The design of the class was driven with unit tests, shown below the PricingServiceImpl source code.  The source code listing below is the final result of TDDing the PricingServiceImpl class.  

The PricingServiceImpl collaborates with a DataAccess type, but it does not take on the responsibility of creating the DataAccess implementation.  That responsibility lies elsewhere in the application.  In this scenario, the unit test will provide the implementation (a mock implementation using Mockito) and inject that instance into the PricingServiceImpl instance.

 1 package examples.mockito;
2
3
import java.math.BigDecimal;
4
5
/**
6 * @author Christopher Bartling, Pintail Consulting LLC
7 * @since Aug 31, 2008 1:18:32 AM
8 */
9 public class PricingServiceImpl implements PricingService {
10
11
private DataAccess dataAccess;
12
13
public void setDataAccess(DataAccess dataAccess) {
14 this.dataAccess = dataAccess;
15 }
16
17
public BigDecimal getPrice(String sku) throws SkuNotFoundException {
18 BigDecimal price = this.dataAccess.getPriceBySku(sku);
19 if (price == null) {
20 throw new SkuNotFoundException("SKU not found.");
21 }
22 return price;
23 }
24 }
25



Finally the unit tests.  Mockito is used to create mock instances of the DataAccess type, suitable for testing the PricingService implementation.  Each unit test drives a new facet of the design for the getPrice(String sku) method.  The method implementation evolved over time as each unit test was introduced.

 1 package examples.mockito;
 2 
 3 import static org.junit.Assert.assertNotNull;
 4 import org.junit.Before;
 5 import org.junit.Test;
 6 import static org.mockito.Mockito.stub;
 7 import static org.mockito.Mockito.verify;
 8 import org.mockito.MockitoAnnotations;
 9 
10 import java.math.BigDecimal;
11 
12 
13 /**
14  * @author Christopher Bartling, Pintail Consulting LLC
15  * @since Aug 31, 2008 1:14:39 AM
16  */
17 public class PricingServiceTests {
18     private static final String SKU = "3283947";
19     private static final String BAD_SKU = "-9999993434";
20     private PricingService systemUnderTest;
21 
22     @MockitoAnnotations.Mock
23     private DataAccess mockedDependency;
24 
25     @Before
26     public void doBeforeEachTestCase() {
27         MockitoAnnotations.initMocks(this);
28         systemUnderTest = new PricingServiceImpl();
29         systemUnderTest.setDataAccess(mockedDependency);
30     }
31 
32     @Test
33     public void getPrice() throws SkuNotFoundException {
34         stub(mockedDependency.getPriceBySku(SKU)).toReturn(new BigDecimal(100));
35         final BigDecimal price = systemUnderTest.getPrice(SKU);
36         // Verify state
37         assertNotNull(price);
38 
39         // Verify behavior
40         verify(mockedDependency).getPriceBySku(SKU);
41     }
42 
43     @Test(expected = SkuNotFoundException.class)
44     public void getPriceNonExistentSkuThrowsException() throws SkuNotFoundException {
45         stub(mockedDependency.getPriceBySku(BAD_SKU)).toReturn(null);
46         final BigDecimal price = systemUnderTest.getPrice(BAD_SKU);
47     }
48 
49     @Test(expected = RuntimeException.class)
50     public void getPriceDataAccessThrowsRuntimeException() throws SkuNotFoundException {
51         stub(mockedDependency.getPriceBySku(SKU)).toThrow(new RuntimeException("Fatal data access exception."));
52         final BigDecimal price = systemUnderTest.getPrice(SKU);
53     }
54 
55 }
56 

The setup of each test case is done in line 27.  Using Mockito annotations, we are able to create the test doubles in one method invocation.  All mock/test spy fields are annotated with @MockitoAnnotations.Mock.  The PricingService implementation instance, the SUT, is created.  The dependency between the PricingService and the DataAccess instance is fulfilled by injecting the mock DataAccess instance into the PricingService implementation.  The SUT is ready for use.

The first test case is a happy path test.  For this test, we configure the stub to return a BigDecimal object, representing the price.  The test is executed in line 39, invoking the PricingService.getPrice(String sku) method on the PricingService implementation.  State and behavior verification occurs in lines 37 and 40.

The second test case verifies that when the call to DataAccess returns null that our PricingService implementation will throw a SkuNotFoundException.  The DataAccess stub object is configured to return null when getPriceBySku(String sku) is invoked.  Our service will be designed to test for null and throw a SkuNotFoundException when this situation occurs.  The second test case verifies that this situation is true.

The third test case verifies that our PricingServiceImpl will allow RuntimeExceptions to bubble out of it.  RuntimeExceptions could be caused by a connection error to the database in DataAccess, for example.  A RuntimeException signals a fatal error has occurred and that fatal error should bubble up through the application and be handled appropriately at a higher level.  PricingService will not take on the responsibility of handling RuntimeExceptions.  The DataAccess stub is configured this time to throw a RuntimeException when getPriceBySku(String sku) is invoked.  Our service will be designed to let this RuntimeException bubble out when this situation occurs.  The third test case verifies this behavior.