JOIN
Get Time
features   
Using Mock Objects with Components

Author
By real_vg
TopCoder Member

The number of components posted on TopCoder Design and Development challenges seems to increase almost every week. While more components means more variety, opportunity, and prize money (all good things), the increased number of components can lead to challenges for developers. When the component you're working on is dependent on another component that is also currently in development (or vice versa) it can make effective unit testing very difficult.

The point of unit testing, of course, is to focus on one individual unit of software at a time to ensure that each piece performs its task correctly. The problem, though, is that these units don't exist in a vacuum. If the component on which your project depends is not yet ready, you will need to simulate it in some way.

This is where mock objects come in. Wikipedia defines mock objects, or "mocks," as "stub-like objects that implement no logic of their own and are used to replace the parts of the system with which the unit under test interacts."

If mock objects are "stub-like," how are they actually different from stubs? Distinguishing mocks from stubs can be tricky, and there are a host of articles and blog entries on the topic (some of which you can explore for yourself, below). For our purposes, it's easiest to understand stubs as static code that gives you one particular response that you expect; mock objects, on the other hand, are dynamically generated and illustrate the range of available responses in an interaction.

Using a mock generally requires manually creating a mock-up implementation of the classes needed for testing. This can be a big effort, as you will need to implement each of the methods of the mock-ups as well as invent some mechanism to check the usage of the mock-up classes. In this article we'll look into an alternative to this labor-intensive approach.

By relying on so-called "Mock Object" libraries, the process of creating mock-ups can be semi-automated, with the mock-up classes created in run time instead of compile time. This approach is backed with various so-called "Mock Objects" libraries. In particular, I'll look at EasyMock, a free open-source software tool that was the first dynamic Mock Object generator. While the examples below are all based on the Java version, a .NET version of EasyMock and specialized .NET libraries such as Rhino Mocks are available as well.

Mock objects in practice

The best way to demonstrate the value of Mock Objects is through a simple example, in which we compare them to manually coded mock-ups. In this example, let's assume we have two components posted for development -- one called "Guitars," the other called "Guitarists." The "Guitars" component defines the "Guitar" interface as:

 
 interface Guitar {
     void play(); 
 }
The "Guitarists" component defines the "Guitarist" interface as:
 
 interface Guitarist {
     void playGuitar(Guitar guitar);
 } 
It also has an implementation of this interface, defined as:
 
 public class CoolGuitarist implements Guitarist {
     void playGuitar(Guitar guitar) {
        guitar.play();
     }
 } 
We want to guarantee that the CoolGuitarist.playGuitar() method will really call the Guitar.play() method, so we need to write some unit tests. Here's where we run into a problem, though, because we don't have any Guitar implementations yet for testing. Let's go ahead and code a mock-up implementation: \
 
 class MockGuitar implements Guitar {
     private int timesPlayed = 0;
     public int getTimesPlayed() {
        return timesPlayed;
     }
     public void play() {
        timesPlayed++;
     }
 } 
Next, we code our unit tests with a method like this:
 
 public void testGuitaristPlay() {
     // Create mock-up object
     MockGuitar guitar = new MockGuitar();
     // Create object to test
     Guitarist guitarist = new CoolGuitarist();
     // Execute method to test
     guitarist.playGuitar(guitar);
     // Check whether the method did its work correctly
     assertEquals(1, guitar.getTimesPlayed());
 } 
Let's see how this test method can be rewritten using Mock Objects library, in particular the EasyMock 2.2 library.
 public void testGuitaristPlay() {
     // Create mock-up object
     Guitar guitar = createMock(Guitar.class);
     // Create object to test
     Guitarist guitarist = new CoolGuitarist();
     // Record the sequence of calls, the mock object should expect
     guitar.play();
     // Replay the sequence of calls
     replay(guitar);
     // Execute method to test
     guitarist.playGuitar(guitar);
     // Verify the sequence of calls
     verify(guitar); 
 } 

While the test method using the mock objects library is a bit longer, it does take fewer lines of code than manually coding the mock class implementation. The use of mock objects also makes things clearer for the people reading the tests (if they are familiar with the technique), as there is no need to look into the implementation of mock classes to understand any assumptions the developer made about the classes' behavior.

Advanced techniques
Just checking to see if a particular method of a mocked object was called is generally insufficient, however. Instead, you will usually need to specify what the method of a mock object should return to its caller. The method can either return some value or throw an exception, and there are means to specify both behaviors. Let's assume that the Guitar interface is a bit expanded (going forward, we will indicate new items with red):

 interface Guitar {
     boolean isTuned();
     void tune() throws StringsTornException;
     void play(); 
 } 
The CoolGuitarist class will then need to be changed to something like:
 public class CoolGuitarist {
     void playGuitar(Guitar guitar) throws StringsTornException {
        if (!guitar.isTuned()) {
            guitar.tune(); 
        }
        guitar.play();
     }
 } 
The Guitarist interface will need to have its playGuitar() method signature updated as well, to indicate that it can throw StringsTornException. The test case will need to test for the new behavior, as follows:
 public void testGuitaristPlay() {
     // Create mock-up object
     Guitar guitar = createMock(Guitar.class);
     // Create object to test
     Guitarist guitarist = new CoolGuitarist();
     // Record the sequence of calls, the mock object should expect
     // Specify that method call should return "false" value
     expect(guitar.isTuned()).andReturn(false);
     guitar.tune();
     guitar.play();
     // Replay the sequence of calls
     replay(guitar);
     // Execute method to test
     guitarist.playGuitar(guitar);
     // Verify the sequence of calls
     verify(guitar); 
 } 
For exceptions the following can be written:
 public void testGuitaristPlayFailure() {
     // Create mock-up object
     Guitar guitar = createMock(Guitar.class);
     // Create object to test
     Guitarist guitarist = new CoolGuitarist();
     // Record the sequence of calls, the mock object should expect
     // Specify that method call should return "false" value
     expect(guitar.isTuned()).andReturn(false);
     expect(guitar.tune()).andThrow(new StringsTornException());
     // Replay the sequence of calls
     replay(guitar);
     try {
         // Execute method to test
         guitarist.playGuitar(guitar);
         fail("StringsTornException should have been thrown");
     } catch (StringsTornException e) {
         // Exception was expected
     }
     // Verify the sequence of calls
     verify(guitar); 
 } 

The method returned by the expect() method provides even more flexibility in addition to that shown above. Beyond the andReturn() and andThrow() methods, it also provides the times() method, which allows you to specify how many method calls are expected. For example, expect(guitar.isTuned()).andReturn(true).times(3), specifies that the isTuned() method should be expected to be called three times and always return "true" value.

The methods can be chained to an even further extent to specify different return values for the same call. For example, expect(guitar.isTuned()).andReturn(true).times(3).andReturn(false) will specify that the method should be expected to be called three times returning "true" value and one time returning "false" value.

There are even more useful features provided by the EasyMock library. For more information, I'd encourage you to check out its documentation.

Conclusions
There are some cases when mock objects are less convenient to use than manually written mock classes, such as instances when getter/setter methods are mostly called. For example, consider the HttpServletRequest class, which has setAttribute() and getAttribute() methods. It would be more convenient and clear to use the setAttribute() method to set the attributes of the request object than it would be to provide the expectations for getAttribute() method calls.

Most of the time, though, using mock objects will help you streamline and accelerate unit testing. EasyMock and similar mock object libraries are easy enough to learn and to use in the unit tests for TopCoder components and, in most cases, their use will result in a significant decrease in code amount and an increase in code clarity.

To learn more, check out some of the references below.

References: