Getting started with mock objects
A mock object is a combination of a spy and a stub. It acts as an indirect output for a code under test, such as a spy, and can also stub methods to return values or throw exceptions, like a stub. A mock object fails a test if an expected method is not invoked or if the parameters of the method don't match.
The following steps demonstrate the test failure scenario:
- Launch Eclipse, open
<work_space>
, and go to the3605OS_TestDoubles
project. - Create a
com.packt.testdoubles.mock
package and aStudentService
class. This class will act as a course register service. The following is the code for theStudentService
class:public class StudentService { private Map<String, List<Student>> studentCouseMap = new HashMap<>(); public void enrollToCourse(String courseName,Student student){ List<Student> list = studentCouseMap.get(courseName); if (list == null) { list = new ArrayList<>(); } if (!list.contains(student)) { list.add(student); } studentCouseMap.put(courseName, list); } }
- Copy the
StudentServiceSpy
class and rename it asStudentServiceMockObject
. Add a new method to verify the method invocations:public void verify(String methodName, int numberOfInvocation){ int actual = invocation(methodName); if(actual != numberOfInvocation){ throw new IllegalStateException(methodName+" was expected ["+numberOfInvocation+"] times but actuallyactaully invoked["+actual+"] times"); } }
- Modify the
StudentService
code to set the mock object, as we did in the spy example:private StudentServiceMockObject mock; public void setMock(StudentServiceMockObject mock) { this.mock = mock; } public void enrollToCourse(String courseName,Student student){ MethodInvocation invocation = new MethodInvocation(); invocation.addParam(courseName).addParam(student).setMethod("enrollToCourse"); mock.registerCall(invocation); …//existing code }
- Create a test to verify the method invocation:
public class StudentServiceTest { StudentService service = new StudentService(); StudentServiceMockObject mockObject = new StudentServiceMockObject(); @Test public void enrolls_students() throws Exception { //create 2 students Student bob = new Student("001", "Robert Anthony"); Student roy = new Student("002", "Roy Noon"); //set mock/spy service.setMock(mockObject); //invoke method twice service.enrollToCourse("english", bob); service.enrollToCourse("history", roy); //assert that the method was invoked twice assertEquals(2, mockObject.invocation("enrollToCourse")); //verify wrong information, that enrollToCourse was //invoked once, but actually it is invoked twice mockObject.verify("enrollToCourse", 1); } }
- Run the test; it will fail, and you will get a verification error. The following screenshot shows the JUnit failure output:
The Mockito framework provides an API for mocking objects. It uses proxy objects to verify the invocation and stub calls.