Unit testing the Eclipse way – Part 2
In Part 2 of this series, Michael Nyika continues to discusses Unit Testing using Eclipse providing practical Scenarios where jMock can can be used to simulate interface calls.
Scenario 1: Use jMock to mock interfaces
Testing the service method in the ServiceClass class is simple. Suppose the test requirement is to assert that the runService() method did not run — in other words, that the Boolean result returned is false. In such a case, the ICollaborator object passed to the runService() method is mocked to expect a call on its method, executeJob(), and return a string other than “success.” In this way, you ensure that the Boolean string false is returned to the test.
The code for the ServiceClassTest class, which is shown below, contains the test logic.
Listing 3. ServiceClassTest class sample code for scenario 1
import org.jmock.Mock;
import org.jmock.cglib.MockObjectTestCase;
public class <span>ServiceClassTest extends MockObjectTestCase</span> {
private ServiceClass serviceClass;
private Mock mockCollaborator;
private ICollaborator collaborator;
public void setUp(){
serviceClass = new ServiceClass();
mockCollaborator = new Mock(ICollaborator.class);
}
public void testRunServiceAndReturnFalse(){
mockCollaborator.expects(once()).method\
("executeJob").will(returnValue("failure"));
collaborator = (ICollaborator)mockCollaborator.proxy();
boolean result = serviceClass.runService(collaborator);
assertFalse(result);
}
}
It is generally a good idea to include a setUp() method in your tests
if common operations are performed across different test cases.
A tearDown() method is also a good idea, but not strictly necessary,
unless you’re running integration tests.
Also note that with jMock and RMock, the framework checks all expectations
across all mock objects at the end of, or during, the test run.
There is no real need to include the verify() method for each mock’s expectations.
When run as a JUnit test, the test passes, as shown below.
Figure 3. Scenario 1 test pass

The ServiceTestClass class extends the jMock CGLIB’s org.jmock.cglib.MockObjectTestCase class. The mockCollaborator is a simple org.jmock.JMock class. Typically, there are two ways to produce mock objects with jMock:
- To mock an interface, use the new Mock(Class.class) method
- To mock a concrete class, use the mock(Class.class, “identifier”) method
It is important to note how a mock proxy is handed to the runService() method in the ServiceClass class. With jMock, you can extract proxy implementations from created mock objects, on which expectations have already been set. This point will be essential in the later scenarios in this article, especially where RMock is concerned.
|
|
Scenario 2: Use jMock to mock a concrete class with a default constructor
Suppose the runService() method in the ServiceClass class accepted only concrete implementations of the Collaborator class. Would jMock be enough to ensure that the previous test passes without changing the expectations? Yes, as long as you can construct the Collaborator class in a simple, default manner.
Change the runService() method in the ServiceClass class to reflect the code below.
Listing 4. Edited ServiceClass class for scenario 2
public class ServiceClass {
public ServiceClass(){
//no-args constructor
}
public boolean runService(Collaborator collaborator){
if("success".equals(collaborator.executeJob())){
return true;
}
else{
return false;
}
}
}
The ServiceClass class’ if…else logic branch remains the same (for clarity). Also, the no-arguments constructor is still in place. Note that it’s not always necessary to have creative logic, such as while…do clauses or for loops to test a class’ methods appropriately. As long as there are method executions against objects the class uses, simple mock expectations are sufficient to test those executions.
You must also change the ServiceClassTest class to suit the scenario, as shown below.
Listing 5. Edited ServiceClassTest class for scenario 2
...
private ServiceClass serviceClass;
private Mock mockCollaborator;
private Collaborator collaborator;
public void setUp(){
serviceClass = new ServiceClass();
mockCollaborator = mock(Collaborator.class, "mockCollaborator");
}
public void testRunServiceAndReturnFalse(){
mockCollaborator.expects(once()).method("executeJob").will(returnValue("failure"));
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
collaborator = (Collaborator)mockCollaborator.proxy();
boolean result = serviceClass.runService(collaborator);
assertFalse(result);
}
}
There are a few points to note here. First, the runService() method signature has changed from earlier. Instead of accepting an ICollaborator interface, it now accepts a concrete class implementation (the Collaborator class). This change is significant as far as the testing framework is concerned. (Note that though anti-polymorphic in nature, we’re using the example of passing a concrete class for the purposes of the example only. It should never be done in a true object-oriented fashion).
Second, the manner of mocking the Collaborator class has changed. The jMock CGLIB library makes it possible to mock the concrete class implementation. The extra String parameter supplied to jMock CGLIB’s mock() method is used as an identifier for the mock object created. When using jMock (and, indeed, RMock), unique identifiers are required per mock object setup within a single test case. This is true for mock objects defined in a common setUp() method or within the actual test method.
Third, the original expectation of the test method has not changed. A false assertion is still required for the test to pass. This is important because by showing how the testing frameworks used are flexible enough to accommodate change under different input while still allowing for constant test results, their true limitations are shown when the inputs cannot be accommodated to produce the same results.
Now, rerun the test as a JUnit test. The test passes, as shown below.
Figure 4. Scenario 2 test pass

In the next scenario, things get slightly more complicated. You use the RMock framework to alleviate what may seem like a difficult situation with relative ease.
Scenario 3: Use jMock and RMock to mock a concrete class with a nondefault constructor
Start by attempting to use jMock, as before, to mock the Collaborator object — only this time, the Collaborator doesn’t have a default no-arguments constructor. Note that the test expectation of a Boolean false result is maintained.
Also suppose that the Collaborator object requires a string and a primitive int as parameters passed to the constructor. Listing 6 shows the changes made to the Collaborator object.
Listing 6. Edited Collaborator class for scenario 3
public class Collaborator{
private String collaboratorString;
private int collaboratorInt;
public Collaborator(String string, int number){
collaboratorString = string;
collaboratorInt = number;
}
public String executeJob(){
return "success";
}
}
The Collaborator class constructor is still quite simple. The class fields are set with incoming parameters. No other logic is necessary here, and its executeJob() function remains the same.
Rerun the test, with all other components of the example remaining the same. The result is a catastrophic test failure, as shown below.
Figure 5. Scenario 3 test failure

The test above was run as a simple JUnit test without code coverage. You can run any of the tests listed in this article with most code coverage tools (for example, Cobertura or EclEmma). There are some issues, however, when it comes to running RMock tests with code coverage inside Eclipse (see Table 1). The code below shows a snippet of the actual stack trace.
Listing 7. Stack trace for test failure in scenario 3
...Superclass has no null constructors but no arguments were given at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:718) at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error: The previous line is longer than the max of 90 characters ---------| at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error: The previous line is longer than the max of 90 characters ---------| at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:660) ..... .....
The reason for the failure is that jMock cannot create a viable mock object from a class definition in which there is no no-arguments constructor. The only way of instantiating the Collaborator object is to provide two simple arguments. You’re now forced to find a way of providing arguments to the mock object instantiation process to achieve the same effect, which is precisely why you use RMock.
Correct the broken test with RMock testing framework
To correct the test, a few modifications are in order. They may appear substantial, but in essence, they’re a relatively simple workaround to leveraging the power of both frameworks to achieve your purpose.
The first change required is to make the test class an RMock TestCase, rather than a jMock CGLIB TestCase. The goal is to enable an easier configuration of those mock objects belonging to RMock within the tests themselves and — more importantly — during their initial setup. Experience shows that it’s easier to construct and use mock objects from both frameworks if the overall TestCase object from which the test class extends belongs to RMock. Moreover, at first glance, it’s a bit easier to quickly determine the flow of mock objects (flow here being used to describe a situation in which you use a mock object as a parameter or even as a return type from other mock objects).
The second change required is to construct (at the very least) an object array holding the actual values of parameters to be passed into the Collaborator class’ constructor. It is also possible, for clarity’s sake, to include a class-types array of the types the constructor accepts and to pass that array, as well as the object array just described as parameters to instantiate the mock Collaborator object.
The third change involves the construction of one or more expectations on the RMock mock object with the correct syntax. And the fourth and final change required is to bring the RMock mock object out of record state into ready state.
Listing 9 shows the final modifications to the ServiceClassTest class. It also shows the introduction of RMock and its related functionality.
Listing 9. Fixing the ServiceClassTest class for scenario 3
...
import com.agical.rmock.extension.junit.RMockTestCase;
public class ServiceClassTest extends RMockTestCase {
private ServiceClass serviceClass;
private Collaborator collaborator;
public void setUp(){
serviceClass = new ServiceClass();
Object[] objectArray = new Object[]{"exampleString", 5};
collaborator =
(Collaborator)intercept(Collaborator.class, objectArray, "mockCollaborator");
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
}
public void testRunServiceAndReturnFalse(){
collaborator.executeJob();
modify().returnValue("failure");
startVerification();
boolean result = serviceClass.runService(collaborator);
assertFalse(result);
}
}
First, note that the test’s expectations still haven’t changed. The import of the RMockTestCase class heralds the introduction of the RMock framework functionality. Next, the test class now extends RMockTestCase, rather than MockObjectTestCase. Later, I’ll show you the reintroduction of MockObjectTestCase within a test case in which the TestClass object is still of type RMockTestCase object.
Within the setUp() method, you instantiate an object array with the actual values the Collaborator class’ constructor needs. That array is summarily fed into RMock’s intercept() method to help instantiate the mock object. The signature of the method is similar to that of the jMock CGLIB mock() method because both methods take on unique mock object identifiers as arguments. The class cast of the mock object into type Collaborator is necessary because the intercept() method returns type Object.
Within the test method itself, testRunServiceAndReturnFalse(), you can see a few more changes. The mock Collaborator object’s executeJob() method is called. At this stage, the mock object is in record state — that is, you’re simply defining the method calls it will expect along the way. So, the mock is recording expectations accordingly. The next line is a notification to the mock object to ensure that when it encounters the executeJob() method, it should return the string failure. Therefore, with RMock, you state an expectation simply by calling the method off the mock object (and passing any parameters it may need), then modifying that expectation to adjust any return types accordingly.
Finally, the RMock method startVerification() is called to place the mock Collaborator object into ready state. The mock object is now ready for use in the ServiceClass class as a real object. The method is absolutely essential and must be called to avoid test initialization failures.
Test your changes
Rerun the ServiceClassTest once again to achieve the final positive result: The parameters you supplied during mock object instantiation made all the difference. Figure 6 shows the positive green light of JUnit.
Figure 6. Scenario 3 test success with RMock

The assertFalse(result) code line represents the same test expectation from scenario 1, and RMock maintains test success as jMock did earlier. In many ways, this is important, but the more dominant point here may be that the agile principle of fixing a broken test has been exercised without changing the test expectation. The only difference is that an alternate framework was employed.
I encourage you to use these frameworks, given their power to produce results for unit testing. Many Java developers aren’t used to writing tests frequently. More often than not, if they do write tests, they’re typically very simple, covering the method’s main functional aim. To test some of the “harder-to-get-to” sections of the code, jMock, and RMock are excellent choices.
Their use would drastically reduce bugs in code and improve your skill in using proven methods to test programming logic. Also, reading documentation and experimenting with the improved versions of these and other frameworks would be a great addition to your developer skills (and reduce any tolerance you may have had for poorly constructed code).



Comments
No comments yet.
Leave a comment