Wednesday, 14 June 2017

EasyMock & PowerMock : Hello World Application

Let me try to explain the usage of mocking with simple example.

EmployeeService.java: This class provides 'getEmployeeFirstNames' method, which takes list of employees as input and return the list of all the first names of employees.

EmployeeUtil.java: It uses the method 'getEmployeeFirstNames' that is implemented by EmployeeService

Suppose let us assume, EmployeeUtil.java class implemented its functionality, by assuming EmployeeService will be implemented in time. Unfortunately, EmployeeService class is not yet implemented. Without mocking, you can't unit test the methods of EmployeeUtil class, since it is dependent on EmployeeService. By Mocking the methods of EmployeeService class we can test Employeeutil class.

MethodNotImplementedException.java
package com.sample.model;

public class MethodNotImplementedException extends RuntimeException {
 private static final long serialVersionUID = 1L;

 public MethodNotImplementedException() {
  super();
 }

 public MethodNotImplementedException(String message) {
  super(message);
 }

}

Employee.java
package com.sample.model;

public class Employee {
 private int id;
 private String firstName;
 private String lastName;
 private double salary;
 private String country;

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public double getSalary() {
  return salary;
 }

 public void setSalary(double salary) {
  this.salary = salary;
 }

 public String getCountry() {
  return country;
 }

 public void setCountry(String country) {
  this.country = country;
 }

 public Employee(int id, String firstName, String lastName, double salary, String country) {
  this.id = id;
  this.firstName = firstName;
  this.lastName = lastName;
  this.salary = salary;
  this.country = country;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Employee [id=").append(id).append(", firstName=").append(firstName).append(", lastName=")
    .append(lastName).append(", salary=").append(salary).append(", country=").append(country).append("]");
  return builder.toString();
 }

}

EmployeeService.java
package com.sample.service;

import java.util.List;

import com.sample.model.Employee;
import com.sample.model.MethodNotImplementedException;

public class EmployeeService {

 public List<String> getEmployeeFirstNames(List<Employee> employees){
  throw new MethodNotImplementedException();
 }
}

EmployeeUtil.java
package com.sample.util;

import java.util.ArrayList;
import java.util.List;

import com.sample.model.Employee;

import com.sample.service.EmployeeService;

public class EmployeeUtil {

 private EmployeeService empService;

 public EmployeeUtil(EmployeeService empService) {
  this.empService = empService;
 }

 /**
  * Return all employee names in upper case.
  * 
  * @param employees
  * @return
  */
 public List<String> getAllEmployeeFirstNamesInUpperCase(List<Employee> employees) {
  List<String> empNamesInLowerCase = empService.getEmployeeFirstNames(employees);
  List<String> result = new ArrayList<>(empNamesInLowerCase.size());

  for (String name : empNamesInLowerCase) {
   if (name == null) {
    continue;
   }

   result.add(name.toUpperCase());
  }

  return result;
 }

}

EmployeeUtilTest.java
package com.sample.util;

import static org.junit.Assert.*;

import java.util.*;

import org.junit.Before;
import org.junit.Test;

import com.sample.service.EmployeeService;

import com.sample.model.Employee;

public class EmployeeUtilTest {

 private List<Employee> employees;
 private List<String> employeeNames;

 @Before
 public void initList() {
  Employee emp1 = new Employee(1, "Hari krishna", "Gurram", 12345, "India");
  Employee emp2 = new Employee(2, "Kiran", "Kumnoor", 234556, "Germany");
  Employee emp3 = new Employee(3, "Soumen", "Mondle", 56789, "India");
  Employee emp4 = new Employee(4, "Sravya", "Guruju", 567890, "Japan");
  Employee emp5 = new Employee(5, "Rachit", "Kumar", 123645, "India");

  employees = new ArrayList<>();
  employeeNames = new ArrayList<>();

  employees.add(emp1);
  employees.add(emp2);
  employees.add(emp3);
  employees.add(emp4);
  employees.add(emp5);

  employeeNames.add("Hari krishna");
  employeeNames.add("Kiran");
  employeeNames.add("Soumen");
  employeeNames.add("Sravya");
  employeeNames.add("Rachit");
 }

 @Test
 public void getAllEmployeeFirstNamesInUpperCase_listOfEmployees_ListOfEmployeeNamesinUpperCase() {
  EmployeeService employeeService = new EmployeeService();

  EmployeeUtil empUtil = new EmployeeUtil(employeeService);

  List<String> employeeFirstNames = empUtil.getAllEmployeeFirstNamesInUpperCase(employees);

  assertEquals(employeeFirstNames.size(), 5);
  assertEquals(employeeFirstNames.get(0), "Hari krishna".toUpperCase());
  assertEquals(employeeFirstNames.get(1), "Kiran".toUpperCase());
  assertEquals(employeeFirstNames.get(2), "Soumen".toUpperCase());
  assertEquals(employeeFirstNames.get(3), "Sravya".toUpperCase());
  assertEquals(employeeFirstNames.get(4), "Rachit".toUpperCase());

 }
}


Run above application, you will end up in 'MethodNotImplementedException'.

com.sample.model.MethodNotImplementedException
 at com.sample.service.EmployeeService.getEmployeeFirstNames(EmployeeService.java:11)
 at com.sample.util.EmployeeUtil.getAllEmployeeFirstNamesInUpperCase(EmployeeUtil.java:25)
 at com.sample.util.EmployeeUtilTest.getAllEmployeeFirstNamesInUpperCase_listOfEmployees_ListOfEmployeeNamesinUpperCase(EmployeeUtilTest.java:49)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

How to resolve above problem?
By creating a mock object to the class 'EmployeeService', we can test EmployeeUtil class.


EmployeeUtilTest.java

package com.sample.util;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.List;

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.sample.model.Employee;
import com.sample.service.EmployeeService;

@RunWith(PowerMockRunner.class)
@PrepareForTest( {EmployeeService.class} )
public class EmployeeUtilTest {

 private List<Employee> employees;
 private List<String> employeeNames;

 @Before
 public void initList() {
  Employee emp1 = new Employee(1, "Hari krishna", "Gurram", 12345, "India");
  Employee emp2 = new Employee(2, "Kiran", "Kumnoor", 234556, "Germany");
  Employee emp3 = new Employee(3, "Soumen", "Mondle", 56789, "India");
  Employee emp4 = new Employee(4, "Sravya", "Guruju", 567890, "Japan");
  Employee emp5 = new Employee(5, "Rachit", "Kumar", 123645, "India");

  employees = new ArrayList<>();
  employeeNames = new ArrayList<>();

  employees.add(emp1);
  employees.add(emp2);
  employees.add(emp3);
  employees.add(emp4);
  employees.add(emp5);

  employeeNames.add("Hari krishna");
  employeeNames.add("Kiran");
  employeeNames.add("Soumen");
  employeeNames.add("Sravya");
  employeeNames.add("Rachit");
 }

 @Test
 public void getAllEmployeeFirstNamesInUpperCase_listOfEmployees_ListOfEmployeeNamesinUpperCase() {
  EmployeeService mockedEmployeeService = EasyMock.createMock(EmployeeService.class);
  EasyMock.expect(mockedEmployeeService.getEmployeeFirstNames(employees)).andReturn(employeeNames);
  EasyMock.replay(mockedEmployeeService);

  EmployeeUtil empUtil = new EmployeeUtil(mockedEmployeeService);

  List<String> employeeFirstNames = empUtil.getAllEmployeeFirstNamesInUpperCase(employees);

  assertEquals(employeeFirstNames.size(), 5);
  assertEquals(employeeFirstNames.get(0), "Hari krishna".toUpperCase());
  assertEquals(employeeFirstNames.get(1), "Kiran".toUpperCase());
  assertEquals(employeeFirstNames.get(2), "Soumen".toUpperCase());
  assertEquals(employeeFirstNames.get(3), "Sravya".toUpperCase());
  assertEquals(employeeFirstNames.get(4), "Rachit".toUpperCase());

 }
}

Let’s try to understand what I did in the above application.

@RunWith(PowerMockRunner.class)
Junit invokes the test of the class with PowerMockRunner class, instead of the runner built into Junit.

@PrepareForTest( {EmployeeService.class} )
This annotation tells PowerMock to prepare certain classes for testing. This annotation can be placed at both test classes and individual test methods. If placed on a class all test methods in this test class will be handled by PowerMock (to allow for testability). To override this behavior for a single method just place a @PrepareForTest annotation on the specific test method. This is useful in situations where for example you'd like to modify class X in test method A but in test method B you want X to be left intact. In situations like this you place a @PrepareForTest on method B.

EmployeeService mockedEmployeeService = EasyMock.createMock(EmployeeService.class);
Above statement creates a mock object of this class.

EasyMock.expect(mockedEmployeeService.getEmployeeFirstNames(employees)).andReturn(employeeNames);
When getEmployeeFirstNames method is called with 'employees' as an argument, then return employeeNames, instead of invoking original method.

EasyMock.replay(mockedEmployeeService);
Activate the mocked behavior. When you write 'EasyMock.expect(mockedEmployeeService.getEmployeeFirstNames(employees)).andReturn(employeeNames);',

You recorded the expected behavior. But, when you write 'EasyMock.replay(mockedEmployeeService);' , you are actually activating it.




Previous                                                 Next                                                 Home

No comments:

Post a Comment