Saturday, July 13, 2013

Mockito Tutorial: TDD Framework for Mocking Functionality and Verifying User Stories

Overview:
This article looks at Mockito, a popular mocking framework for testing your Java code. Mockito API promotes writing clean and readable tests.

Here, we will look at a simple example how you could leverage the Mockito API to mock functionality in your tests.

Sample code can be found here.

Project Structure:
 org.fazlan.mockito.sample  
 ├── pom.xml  
 └── src  
   ├── main  
   │   └── java  
   │       └── org  
   │           └── fazlan  
   │               ├── CreditChecker.java  
   │               ├── CreditCheckException.java  
   │               └── Order.java  
   └── test  
     └── java  
       └── org  
         └── fazlan  
           └── OrderTest.java  

Step 1: Creating the Project

 mvn archetype:generate -DartifactId=org.fazlan.mockito.sample -DgroupId=org.fazlan -Dversion=1.0-SNAPSHOT -DinteractiveMode=false  

Step 2: Updating the Maven dependencies

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>org.fazlan</groupId>  
  <artifactId>org.fazlan.mockito.sample</artifactId>  
  <version>1.0-SNAPSHOT</version>  
  <packaging>jar</packaging>  
  <name>mockito-sample</name>  
  <url>http://maven.apache.org</url>  
  <properties>  
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  </properties>  
  <dependencies>  
    <dependency>  
      <groupId>org.testng</groupId>  
      <artifactId>testng</artifactId>  
      <version>6.8.5</version>  
      <scope>test</scope>  
    </dependency>  
    <dependency>  
      <groupId>org.mockito</groupId>  
      <artifactId>mockito-core</artifactId>  
      <version>1.9.5</version>  
    </dependency>  
  </dependencies>  
 </project>  

Step 3: Defining the User story for placing an order

The following is a simple user story for placing an order, the story is based on an ordering system.

The story assumes that the system has clients and they have credit accounts associated with them. To successfully place an order, the clients should have sufficient funds, otherwise, the processing would fail. Thus, a credit check is performed before placing the order.

 Narrative:  
 In order to purchase goods for my needs  
 As a credit worthy client  
 I want to place an order

Step 4: Writing the test to verify the story

The following is a simple test for verifying the story. It's decorated using Mockito annotations, and they keep the code clean and easy to read.

 package org.fazlan; 
 
 import org.mockito.InjectMocks;  
 import org.testng.annotations.BeforeMethod;  
 import org.testng.annotations.Test;  
 import java.util.UUID; 
 
 import static junit.framework.Assert.assertEquals;  
 import static junit.framework.Assert.assertNotNull;  
 import static org.fazlan.Order.createOrderFor;
  
 import static org.mockito.Mockito.*;  
 import static org.mockito.MockitoAnnotations.Mock;  
 import static org.mockito.MockitoAnnotations.initMocks;  

 public class OrderTest {  

   private String clientNumber;  
   //injected by mockito  
   @Mock  
   private CreditChecker creditChecker;
  
   @InjectMocks  
   private Order order;  

   @BeforeMethod  
   public void setUp() {  
     //given  
     generateClientNumber();  
     order = createOrderFor(getClientNumber());  
     //initialises the mocks and injects them to the designated targets
     initMocks(this);
   }  

   @Test  
   public void shouldCreateOrderGivenClientNumber() {  
     //then  
     assertNotNull(order);  
     assertEquals(clientNumber, order.getClientNumber());  
   }  

   @Test(expectedExceptions = {CreditCheckException.class})  
   public void shouldNotPlaceTheOrderGivenClientCreditCheckFails() {  
     //given  
     theCreditCheckWouldThrowExceptionWhenCalled();  
     //when  
     order.place();  
   }  

   @Test  
   public void shouldPlaceTheOrderGivenClientCreditCheckPasses() {  
     //given  
     theCreditCheckWouldPassWhenCalled();  
     //when  
     order.place();  
     //then  
     assertNotNull(order.getOrderNumber());  
     verify(creditChecker).check(getClientNumber());  
   }  

   private void theCreditCheckWouldThrowExceptionWhenCalled() {  
     doThrow(new CreditCheckException()).when(creditChecker).check(getClientNumber());  
   }  

   private void theCreditCheckWouldPassWhenCalled() {  
     doNothing().when(creditChecker).check(getClientNumber());  
   }  

   private void generateClientNumber() {  
     clientNumber = UUID.randomUUID().toString();  
   }  

   private String getClientNumber() {  
     return clientNumber;  
   }  
 }  

IMPORTANT NOTE
Here, the CreditChecker is just an Interface, and there is no implementation. It could be either the implementation is not available at this point of time or, it's been developed by someone else, which we really don't care.

 package org.fazlan;  

 public interface CreditChecker {  
   void check(String clientNumber);  
 }  

However, what we are interested is in how the order placement functionality would react based on the outcome of the credit check. Thus, we can use Mockito to mock the credit check and make it behave the way we intend in order to test our logic while placing an order.

This is the beauty of a mocking framework, without having to implement the actual details you can still work on your tests. This is specially true in a TDD environment, where you want to  test a piece of code in isolation without having to care about it's infrastructure code or dependencies.

Step 5: Using Mockito to mock and verify the story

The tests are self-explanatory, and now lets take a close look the code.

   @Mock   
   private CreditChecker creditChecker;  

   @InjectMocks   
   private Order order;   

   @BeforeMethod   
   public void setUp() {   

    //given   
    generateClientNumber();   

    order = createOrderFor(getClientNumber());   

    //initialises the mocks and injects them to the designated targets  
    initMocks(this);  
   }   

The above code basically says, that the test should perform the following, and for each test (since it's been annotated with @BeforeMethod in testng framework), it should create an instance of an Order and initialise it with the mocked CreditChecker.
  • @Mock - Mock the interface CreditChecker. During the runtime, Mockito will generate a proxy that acts as an actual implementation of the interface and make it behave as we have specified.
  • @InjectMocks - Inject the mocked dependencies used in this instance. (e.i: creditChecker).
  • initMocks(this) - Initialise the above annotations for mocks and the target instance to be injected with in this test instance.
Lets assume, our business logic is such that if the client has insufficient funds, then the credit check should fail by throwing an CreditCheckException, otherwise, it'll do nothing.

Based on the above business logic, we advise the Mockito framework to behave exactly as per our expectation when we perform the credit check logic for different scenarios.

Scenario: When the credit check fails:

Here, we advise the Mockito framework to throw a CreditCheckException exception when the check() operation is called on the mocked creditCheck instance, so that we can test our order placement logic when credit check fails.

   @Test(expectedExceptions = {CreditCheckException.class})  
   public void shouldNotPlaceTheOrderGivenClientCreditCheckFails() {  
     //given  
     theCreditCheckWouldThrowExceptionWhenCalled();  
     //when  
     order.place();  
   }  
  
   private void theCreditCheckWouldThrowExceptionWhenCalled() {  
     doThrow(new CreditCheckException()).when(creditChecker).check(getClientNumber());  
   }  
 

Scenario: When the credit check passes:

Similarly, here we advise the Mockito framework to do nothing when the check() operation is called on the mocked creditCheck instance.

   @Test  
   public void shouldPlaceTheOrderGivenClientCreditCheckPasses() {  
     //given  
     theCreditCheckWouldPassWhenCalled();  
     //when  
     order.place();  
     //then  
     assertNotNull(order.getOrderNumber());  
     verify(creditChecker).check(getClientNumber());  
   } 

   private void theCreditCheckWouldPassWhenCalled() {  
     doNothing().when(creditChecker).check(getClientNumber());  
   }  

You can find more about the Mockito API documentation here.

Step 6: Based on the above tests, we can develop our Order.java as follows,

Following is a simple implementation of the Order.java, that has a method called 'place' to place the order against a given client.

 package org.fazlan;  
 
 import java.util.UUID;  

 //domain driven entity - @see DDD - Domain Driven Design for more.  
 public class Order {  

   private String orderNumber;  
   private String clientNumber;  
   private CreditChecker creditChecker;  

   private Order(String clientNumber) {  
     this.clientNumber = clientNumber;  
   }  

   public void place() {  
     doCreditCheck();  

     assignOrderNumber();  

     //do some processing and save the order  
   }  

   private void doCreditCheck() {  
     creditChecker.check(getClientNumber());  
   }  

   private void assignOrderNumber() {  
     orderNumber = UUID.randomUUID().toString();  
   }  

   public static Order createOrderFor(String clientNumber) {  
     return new Order(clientNumber);  
   }  

   public String getClientNumber() {  
     return clientNumber;  
   }  

   public String getOrderNumber() {  
     return orderNumber;  
   }  

   protected void setCreditChecker(CreditChecker creditChecker) {  
     this.creditChecker = creditChecker;  
   }  
 }  

Summary:
This was a brief article on how to make use of Mockito as a framework for mocking functionality in your tests in a TDD environment.

Sample code can be found here.

1 comment: