Monday, 13 April 2009

Mocking

In this article I will explain a unit testing concept called Mocking. We will also discuss a couple of examples based on EasyMock library.

Unit testing is an integral part of any software development cycle. It is the process using, which a programmer makes sure that the piece of code he/she has developed does what it is supposed to do. In more specific terms a unit test can be defined as the test of a single isolated component that can be done repeatedly. There are various tools available for doing unit testing like Nunit, JUnit etc.

Testing units of code often is a problem because mostly, the components of a software application do not work in isolation but collaborate with other components to get the job done. But in unit testing we don’t want to test the depending objects, but the unit under test. Mocking provides a solution to this problem. All the collaborating objects which are not to be tested can be passed as the ’mock’ objects after setting up their behaviour for the life cycle of the test.
Mock objects are not to be confused with mocks or stubs, which is another library to facilitate the testing of a unit with dependencies.

The basic idea behind Mock Objects is that the dependencies are resolved on the fly with in the test itself. EasyMock provides Mock Objects for interfaces in JUnit tests by generating them on the fly using Java's proxy mechanism.

Let me explain this concept by a very basic example. I have used Easymock for mocking the objects. Consider the following class:
public class Employee 
{
public Employee(String fName, String lName)
{
firstName = fName;
lastName = lName; 
}
private String firstName = null;
private String lastName = null;

public String getFirstName()
{
return firstName;
}


public String getLastName()
{
return lastName;
}

public String displayNameWithDepartment(IDepartment dpt)
{
return firstName+” ”+lastName+” ”+dpt.getName();
}
}

There is an interface IDepartment which is used by our Employee class.
public class IDepartment 
{
public String getName();
public String getHODName();
}

Now let’s consider testing of Employee class. What do we want to test, and how do we go about it. We are mainly interested in the way that our implementation class collaborates with its dependencies. We need to make sure that our class behaves correctly, when interacting with these parameters. We want to make sure that the right methods are called in the right order with the right parameters, but it is not used.
Obviously, in order to test the Employee class’s behaviour we will have to pass it a IDepartment reference but which need not be an actual object.
Lets create an example JUnit test, which shows the EasyMock at work
import static org.easymock.EasyMock.*;
import junit.framework.*;
//other imports omitted

public class EasyMockTest extends TestCase
{
public void testWithMockObjects() throws Exception
{
// strict mock forces us to specify the correct order of method calls
IDepartment dpt = createStrictMock(IDepartment.class); //1

expect(dpt.getName()).andReturn("Development"); // 2

//unexpected method calls after this point will throw exceptions
replay(dpt); //3

Employee emp = new Employee(“Michael”, “Sheen”); //4

assert(emp.displayNameWithDepartment(dpt), “Michael Sheen Development”) //5
//check that the behaviour expected is actually
verify(request); //6
}
//more test methods omitted ...
}

EasyMockTest class tests the behaviour of the displayNameWithDepartment() method in a specific scenario.

Let’s walk through each line of code and find out what it is doing.
1. We ask EasyMock to create dynamic proxy implementing IDepartment, which starts in record mode.
2. Record a method call along with the expected result.
3. replay() call tells EasyMock to stop recording. If we don’t call replay(), a call to dpt.getName() would return null.
4. Create the Employee object that needs to be tested.
5. Call on the mock object returns the set value. If it gets a call it does not expects, it throws an exception.
6. Verifies that only the method calls which were recorded were played. If we make a method call which was not recorded, an ‘Unexpected method call’ error is thrown. Moreover all the method calls which were recorded need to be made.

Note: Methods on the mock object would return the result in the same order in which the expectations were recorded/set on them. If the order does not match our test case is going to fail.
For example if our test case contains following lines of code, it’s going to fail:
expect(dpt.getName()).andReturn("Development"); //Development first
expect(dpt.getName()).andReturn("HR"); //HR second  
replay(dpt);
Employee emp = new Employee("Michael", "Sheen");
assertEquals(emp.displayNameWithDepartment(dpt), "Michael Sheen HR"); //HR first
assertEquals(emp.displayNameWithDepartment(dpt), "Michael Sheen Development");
verify(dpt);

Multiple calls
If a method on a mock object is to be called multiple times, we can use times() method. So if in the above example, I want to set multiple expectations with same arguments, I can use following the following line of code
expect(dpt.getName()).andReturn("Development").times(2);

reset method()
In the above example, the test method has a single replay() and verify() call with single expectation setting and verification phases. Sometimes it is convenient to reuse the same mock object over multiple expectation setting, replay and verification cycles. But the problem is once a mock object has been verified, it cannot record a method call. If we try to do that, an ‘Unexpected method call name’ error is thrown.
reset() method is used to set the mock object back to the expectation setting mode.
For example consider the following two scenarios:
Without reset()
expect(dpt.getName()).andReturn("Development");
replay(dpt);
Employee emp = new Employee("Michael", "Sheen");
assertEquals(emp.displayNameWithDepartment(dpt), " Michael Sheen Development");
verify(dpt);
expect(dpt.getName()).andReturn("Development");  //Error
replay(dpt);
assertEquals(emp.displayNameWithDepartment(dpt), "Michael Sheen Development");
verify(dpt);
With reset()
expect(dpt.getName()).andReturn("Development");
replay(dpt);
Employee emp = new Employee("Michael", "Sheen");
assertEquals(emp.displayNameWithDepartment(dpt), "Michael Sheen Development");
verify(dpt);
reset(dpt); //resetting the mock object back to the recording mode
expect(dpt.getName()).andReturn("Development");
replay(dpt);
assertEquals(emp.displayNameWithDepartment(dpt), "Michael Sheen Development");
verify(dpt);
Nice mocks and Strict mocks
You may have observed we used createStrictMock() to create the Mock objects. There are two other ways to create mock objects.
  • Nice mock
  • Default mock object
All these mocking strategies basically differ in two ways

Optionality
Nice mock allows you to omit expected method calls. If a method is called, but no expectation has been set, then the nice mock simply returns the default value for the type, if the invoked method returns a primitive, or null for an object.
For example, if we change our DepartmentImpl to look like this
public class DepartmentImpl implements IDepartment{
private String name = null;
private String HOD = null;
public String getName() 
{
return name;
}

public String getHODName() 
{
return HOD;
}
}
and change the testWithMockObjects to following
public void testWithMockObjects() throws Exception
{
IDepartment dpt = createNiceMock(IDepartment.class);
replay(dpt);
Employee emp = new Employee(“Michael”, “Sheen”);
assert(emp.displayNameWithDepartment(dpt), “Michael Sheen”)
verify(request);
}
Ordering
Expected invocations on strict mocks need to be specified in the correct order, that is, in the same order that they are executed. In the case of default and nice mocks its not mandatory.

Stubs
Strict mocks are great for defining rigorous tests, but at times applying them can lead to overkill. For example our mock objects contain a bunch of methods, with only some of these of interest. So how can we stop our tests from failing as a result of unexpected behaviour of these "other" methods? This is where Stubs are used.
Consider the code snippet below.
expect(dpt.getName()).andStubReturn("Development"); 
replay(dpt);
Employee emp = new Employee("Michael", "Sheen");
assertEquals(emp.displayNameWithDepartment(dpt), "Michael Sheen A");
...
...
...
...
verify(dpt);
The first line says, if dpt.getName(...) is called one or more times, then each time simply return the String "Development". However, if the method is not called, then don't throw an error. This gives us a lot of flexibility in stubbing out interaction with the mock objects which are irrelevant to the test in consideration.

Summary
Mocking is a powerful technique for helping to obtain high levels of test coverage for our code without worrying much about resolving the associated dependencies. It allows us to specify the behaviour of objects collaborating with our classes under test with a great deal of ease and with a lot more control.
The aim of this article is provide a brief overview of Mocking and overcome the hurdles faced in putting EasyMock to work in their applications.

If you find any problem in the code, or disagree with anything I have mentioned please write to me at ravinder.rawat@gmail.com. You might help me in becoming a more learned person.

1 comment:

Anonymous said...

One of the best articles I have ever come across on Mocking

Thanks very much Ravi.

 
Technology