Recently I wanted to be able to do something I had done for quite awhile using Simpletest in PHP … create partial mocks that mock out just a specified method on an object and replace it with canned data (a stub). So far I hadn’t found something to let me do this in the java world short of subclassing the actual object and overriding the method in an inner class. I’ve been against doing that for the most part because I believe mocks should be dynamic rather than hard coded, although where this belief comes from is beyond me.
Anyhow, Rmock has what is known as intercepting objects which allow you to intercept only specified methods, while calling the real methods on the ones you don’t specify. This is perfect in cases where your class acesses a singleton, or is responsible for the creation and use of a collaborator. I tend to move the singleton access or object creation to seperate methods, then these can be intercepted to return mock objects (or whatever else you want).
So, for a quick and dirty example I’ll make a simple class that contains three methods … sayHello(), sayWorld(), and printMessage(). Naturally, this is a very simple example and not quite pragmatic, but serves the purpose of illustrating intercepting objects without having to convey the meaning of the objects used.
So, we have this class:
public class HelloWorld {
public String printMessage(){
return sayHello() + " " + sayWorld();
}
public String sayWorld() {
return "World";
}
public String sayHello() {
return "Hello";
}
}
And an companying unit test:
import junit.framework.*;
public class HelloWorldTest extends TestCase {
public void testPrintMessage(){
HelloWorld hello = new HelloWorld();
assertEquals("Hello World", hello.printMessage());
}
}
Now, let’s assume that sayWorld() accesses an external resource, and let’s mock it out. So first we start by making our test extend RMockTestCase:
public class HelloWorldTest extends RMockTestCase
And then, instead of creating the object directly, we use the intercept method provided by RMockTestCase and pass in the name for our mock object:
public void testPrintMessage(){
HelloWorld hello = (HelloWorld) intercept(HelloWorld.class, "HelloWorld");
startVerification();
assertEquals("Hello World", hello.printMessage());
}
Also, you notice I call startVerification() before I use the object, which is discussed on the RMock site and I will not cover here.
Now that we are intercepting the object now (but still calling the actual methods) let’s mock out the sayWorld() method to return “Bob”:
public void testPrintMessage(){
HelloWorld hello = (HelloWorld) intercept(HelloWorld.class, "HelloWorld");
hello.sayWorld();
modify().returnValue("Bob");
startVerification();
assertEquals("Hello World", hello.printMessage());
}
Now our assertion fails! Just a quick change to the string we are expecting to get back changes that:
assertEquals("Hello Bob", hello.printMessage());
And we see green.
If we had been using a more complex object, or say a DAO or InputStream, we would have created a mock object and returned that from the method.
So here is our final version of our test that we now have:
import com.agical.rmock.extension.junit.*;
public class HelloWorldTest extends RMockTestCase {
public void testPrintMessage(){
HelloWorld hello = (HelloWorld) intercept(HelloWorld.class, "HelloWorld");
hello.sayWorld();
modify().returnValue("Bob");
startVerification();
assertEquals("Hello Bob", hello.printMessage());
}
}