Tuesday 17 June 2014

EasyMock : Simple Tutorial

EasyMock provides an easy way to create Mock Objects for interfaces and classes generating them on the fly. It is a mock framework which can easily be used in conjunction with JUnit. It is a perfect fit for Test-Driven development.

In this post, we will see how EasyMock can be used to easily test our Java application. EasyMock is helpful in situations wherein you want to mock some of the objects in an application for testing purposes. Service layer classes which often talk to external database/server can easily be mocked. One can easily define the behavior which is expected in response to a certain event. 

I have made use of PowerMock to invoke methods in an object. We need EasyMock, Objenesis and Cglib libraries added to the classpath.You can find the complete source code here.

This is how you create a mock object and specify what is to be returned in response to a certain expected event.

Retailer retailer = EasyMock.createMock(Retailer.class);

EasyMock.expect(retailer.getPriceForProduct("101")).andReturn(220);

The createMock() creates a mock retailer object. Whenever a call is made to getPriceForProduct() with "101" as the productId argument the returned value will be 220 as set by EasyMock.

Also, we need to activate our mock object before making its use using replay() method.This replay() is to be done after specifying all the expectations and returns.

EasyMock.replay(retailer);

Here is an Example. We have a customer class which has retailer object as its member.
package com.nirman.easymock;

public class Customer {

 String name;
 Retailer retailer;

 public int getProductPrice(String productId) throws Exception{
  int price = retailer.getPriceForProduct(productId);
  return price;
 }
 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Retailer getRetailer() {
  return retailer;
 }

 public void setRetailer(Retailer retailer) {
  this.retailer = retailer;
 }

}

This is the Retailer class.
package com.nirman.easymock;

public class Retailer {

 private int taxes_in_percent = 10;

 public int getPriceForProduct(String productId) throws Exception {
  int price;
  if (productId.equals("101")) {
   price = getPrice(100);
  } else if (productId.equals("102")) {
   price = getPrice(200);
  } else if (productId.equals("103")) {
   price = getPrice(300);
  } else {
   price = 0;
  }
  return price;
 }

 private int getPrice(int basePrice) {
  int finalPrice = basePrice + ((basePrice * getTaxRate()) / 100);
  return finalPrice;
 }

 public int getTaxRate() {
  return taxes_in_percent;
 }
}

There is a method getPriceForProduct(String productId) which takes productId and returns its price after adding the taxes that are applicable. We will mock this method using EasyMock. 

This is my JUnit -
package com.nirman.easymock;

import org.easymock.EasyMock;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import static org.junit.Assert.*;

public class TestRetailer {

 // Without any mocks
 @Test
 public void testGetPriceForProduct() throws Exception {
  Customer customer = new Customer();
  Retailer retailer = new Retailer();
  customer.setRetailer(retailer);
  int actual = 0;

  String productId = "101";
  actual = Whitebox. invokeMethod(customer, "getProductPrice",
    productId);

  int expected = 110;
  assertEquals(expected, actual);
 }

 // Mocked the getPriceForProduct() in retailer
 @Test
 public void testGetPriceForProductEasyMock() throws Exception {
  Customer customer = new Customer();
  Retailer retailer = EasyMock.createMock(Retailer.class);
  customer.setRetailer(retailer);
  EasyMock.expect(retailer.getPriceForProduct("101")).andReturn(220);
  EasyMock.replay(retailer);

  int actual = 0;

  String productId = "101";
  actual = Whitebox. invokeMethod(customer, "getProductPrice",
    productId);

  int expected = 220;
  assertEquals(expected, actual);
 }

 // Assertion Error. As the mock is not activated, actual returned is 0;
 @Test
 public void testGetPriceForProductAssertionError() throws Exception {
  Customer customer = new Customer();
  Retailer retailer = EasyMock.createMock(Retailer.class);
  customer.setRetailer(retailer);
  EasyMock.expect(retailer.getPriceForProduct("401")).andReturn(220);
  int actual = 0;

  String productId = "401";
  actual = Whitebox. invokeMethod(customer, "getProductPrice",
    productId);
  int expected = 220;
  assertEquals(expected, actual);
 }

}

Note that the third Test here for testGetPriceForProductAssertionError() will result in an assertion error and will not pass as the mock was not activated in that case resulting in an unexpected behavior.

Thanks and Happy Coding !!

No comments:

Post a Comment