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.
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.
No comments:
Post a Comment