Home > Java, Testing > Testing with jMock

Testing with jMock

jMock is a lightweight testing framework that lets you do test driven development in Java. It lets you mock the actual objects, that is, create fake ones, and use them in your test cases instead of the real ones. This is an extremely useful and handy approach since you can actually work on your project without having the actual class implementations in hand; jMock lets you define the expected behaviour. The only thing you need to have is the interfaces of the classes you are mocking. JMock works only with interfaces (no abstract or concrete classes) so you need to have the interfaces ready before you go on testing.

Lets think of a very simple example. Lets say that we have a class called Computer that computes a few things. It does a multiplication, appends the result to the end of a string, reverses the string and returns it to the caller. Ok, it’s a trivial and naive example but it will demonstrate the purpose of jmock. Before we start you need to download the jMock 2.4 release and junit. You can download any version of JUnit; I have the 3.8 installed on my computer so I will be using this one throughtout this post. But replacing the 3.8 JUnit code with the latest annotated version of JUnit should not be hard to do. After downloading the zip files you will need to put the junit.jar, jmock-2.4.0.jar, and hamcrest-library-1.1.jar in your classpath. The jMock site states that you should also include the hamcrest-core-1.1.jar but the tests I wrote work fine without it.

Lets start by defining first our test class. We call it ComputerTest since it will be testing the Computer class. jMock will be used with the ComputerTest class. We will need to create all the mock objects in there. It should be a very simple class that extends the JUnit TestCase for the beginning

public class ComputerTest extends TestCase
{

}

Now it’s time to think about the functionality of the test method. The use case said that the Computer clas will need to do a multiplication, append the result to a string and then reverse this string. Lets assume that we need three different classes to do that. We need a Calculator class, an Appender class and a Reverser class. These should be interfaces (to be able to use them with jMock) and each one of them should define at least one method. 

interface Calculator
{
    /**
     * Multiplies two integer values and returns the result.
     * @param a the first value.
     * @param b the second value.
     * @return the product of the multiplication.
     */
    Integer multiply(final int a, final int b);
}

interface Appender
{
    /**
     * Appends an integer value to the end of the string.
     * @param value the integer value to be appended.
     * @return the string with the appended value.
     */
    String append(final Integer value);
}

public interface Reverser
{
    /**
     * Reverses a string.
     * @param toBeReversed the string to be reversed.
     * @return the reversed string.
     */
    String reverse(String toBeReversed);
}

Now in out test class we will need to mock these interfaces. We can do that by using the Mockery object. Once the objects are mocked they can be passed on to the Computer class that will use them. Note that we can only test for objects that have been mocked by using Mockery since jMock only knows about these objects. If we try to test a non-mocked object jMock will complain. Lets define in our test class and mock the objects we are interested in

public class ComputerTest extends TestCase
{
    private Mockery mockery;
    private Calculator calculator;
    private Appender appender;
    private Reverser reverser;
    private Computer computer;

    /**
     * {@inheritDoc}
     */
    @Override
    public void setUp()
    {
        mockery = new Mockery();
        calculator = mockery.mock(Calculator.class);
        appender = mockery.mock(Appender.class);
        reverser = mockery.mock(Reverser.class);
        
        computer = new Computer(calculator, appender, reverser);
    }
}

We can see that by using mockery.mock we can create mock objects that we pass into our Computer class. Since we pass these objects using the constructor the Computer class should look something like

public class Computer
{
    private Calculator calculator;
    private Appender appender;
    private Reverser reverser;

    /**
     * Constructor.
     * 
     * @param calculator the calculator class.
     * @param appender the appender class.
     * @param reverser the reverser class.
     */
    public Computer(final Calculator calculator, final Appender appender, final Reverser reverser)
    {
        this.calculator = calculator;
        this.appender = appender;
        this.reverser = reverser;
    }
}

Now it’s time to think what we need to test. The end result is a reversed string, so Computer should have a method that returns a string. This is the method that should be tested from the test class. We can call this method compute. The compute method should multiply, append and then reverse the string. To do that it will need to call the methods on the objects passed into the Computer. We can test these steps one by one in our test method. For the first step (multiply) we can have something like the following

public class ComputerTest extends TestCase
{
    private Mockery mockery;
    private Calculator calculator;
    private Appender appender;
    private Reverser reverser;
    private Computer computer;

    /**
     * {@inheritDoc}
     */
    @Override
    public void setUp()
    {
        mockery = new Mockery();
        calculator = mockery.mock(Calculator.class);
        appender = mockery.mock(Appender.class);
        reverser = mockery.mock(Reverser.class);

        computer = new Computer(calculator, appender, reverser);
    }

    /**
     * Tests the compute method.
     */
    public void testCompute()
    {
        final Integer product = Integer.valueOf(200);
        final int a = 10, b = 20;
        
        mockery.checking(new Expectations(){
            {
                one(calculator).multiply(a, b);
                will(returnValue(product));
            }
        });

        computer.compute(a, b);
        mockery.assertIsSatisfied();
    }
}

The mockey.checking() method checks for the expectations we set. We set these expectations as an Expectations object, in a initialisation block of code (there are other ways to set expectations, for these you can have a look at JMock’s web site). Remember again that we do not test the functionality of the class but we test the behaviour of it. We don’t test if the class returns a valid result, we only test  that the class performs the steps necessary to return a result. So in our example above when the statement computer.compute(a, b) is called jMock tests and asserts (with the mockery.assertisSatisifed() statement) that our expectations are satisfied. In the above case it will test if the multiply method of the calculator object is called one, and only one, time in the Computer‘s compute method with the parameters 10 and 20. If it is then the test case will succeed, otherwise it will fail. If the compute method is something like the following

public String compute(final int a, final int b)
{
    return "";
}

then the test method will fail with the error message 

not all expectations were satisfied
expectations:
  expected exactly 1 time, never invoked: calculator.multiply(<10>, <20>); returns <200>

which means exactly what the error says. jMock expected a call of the calculator.multiply method one time but it was never called. This means that the compute method does not call the multiply method on the Calculator object. To resolve this issue and make the test case pass we should add a statement in the compute method that does the call

public String compute(final int a, final int b)
{
    Integer product = calculator.multiply(a, b);
    return "";
}

By adding a calculator.multiply statement the test should pass. Also note that we can control the output of the call by using the will(returnValue(product)) statement. This lets us specify the values we want to return from a method call. We say to jMock that the call to calculator.multiply will return a value that is equal to the product variable. If you do not need to use this value later in your application you might as well ignore the will(return) call.

Lets add the last two calls to our test method and finish it.

public void testCompute()
{
    final Integer product = Integer.valueOf(200);
    final int a = 10, b = 20;
    final String someString = "a string ";

    mockery.checking(new Expectations(){
        {
            one(calculator).multiply(a, b);
            will(returnValue(product));

            one(appender).append(product);
            String appendedString = someString + product.toString();
            will(returnValue(appendedString));

            one(reverser).reverse(appendedString);
        }

    });

    computer.compute(a, b);
    mockery.assertIsSatisfied();
}

Here we simulate the last two calls to the objects we are interested in. One call to the append method of the appender object and a second call to the reverse method of the reverser object. Of course this will fail since the compute method does not have these calls. We will need to change it to the following

/**
 * Calculates the product of two numbers, appends it to a String and then reverses this string.
 * 
 * @param a the first number.
 * @param b the second number.
 * @return a reversed string.
 */
public String compute(final int a, final int b)
{
    Integer product = calculator.multiply(a, b);
    String s = appender.append(product);
    return reverser.reverse(s);
}

This will make the test code pass since all the behaviour is satisfied (all the objects call the methods specified). 
 
I really hope you will find jMock useful since it’s extremelly powerful. If you are new to testing you might find it a bit steep to learn jMock but the secret is to keep at it. Keep experimenting and try different things. If all else fails leave a comment here and if I know I will try ot help you. The full code of the example is shown below

package jmock24;
 
import junit.framework.TestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;

/**
 *
 * @author panos
 */
public class ComputerTest extends TestCase
{
    private Mockery mockery;
    private Calculator calculator;
    private Appender appender;
    private Reverser reverser;
    private Computer computer;
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void setUp()
    {
        mockery = new Mockery();
        calculator = mockery.mock(Calculator.class);
        appender = mockery.mock(Appender.class);
        reverser = mockery.mock(Reverser.class);
        
        computer = new Computer(calculator, appender, reverser);
    }
 
    /**
     * Tests the compute method.
     */
    public void testCompute()
    {
        final Integer product = Integer.valueOf(200);
        final int a = 10, b = 20;
        final String someString = "a string ";

        mockery.checking(new Expectations(){
            {
                one(calculator).multiply(a, b);
                will(returnValue(product));

                one(appender).append(product);
                String appendedString = someString + product.toString();
                will(returnValue(appendedString));

                one(reverser).reverse(appendedString);
            }
        });

        computer.compute(a, b);
        mockery.assertIsSatisfied();
    }
}

package jmock24;

/**
 *
 * @author panos
 */
interface Calculator
{
    /**
     * Multiplies two integer values and returns the result.
     * @param a the first value.
     * @param b the second value.
     * @return the product of the multiplication.
     */
    Integer multiply(final int a, final int b);
}

package jmock24;

/**
 *
 * @author panos
 */
interface Appender
{
    /**
     * Appends an integer value to the end of the string.
     * @param value the integer value to be appended.
     * @return the string with the appended value.
     */
    String append(final Integer value);
}

package jmock24;

/**
 *
 * @author panos
 */public interface Reverser
{
    /**
     * Reverses a string.
     * @param toBeReversed the string to be reversed.
     * @return the reversed string.
     */
    String reverse(String toBeReversed);
}

package jmock24;

/**
 *
 * @author panos
 */
public class Computer
{
    private Calculator calculator;
    private Appender appender;
    private Reverser reverser;

    /**
     * Constructor.
     * 
     * @param calculator the calculator class.
     * @param appender the appender class.
     * @param reverser the reverser class.
     */
    public Computer(final Calculator calculator, final Appender appender, final Reverser reverser)
    {
        this.calculator = calculator;
        this.appender = appender;
        this.reverser = reverser;
    }

    /**
     * Calculates the product of two numbers, appends it to a String and then reverses this string.
     * 
     * @param a the first number.
     * @param b the second number.
     * @return a reversed string.
     */
    public String compute(final int a, final int b)
    {
        Integer product = calculator.multiply(a, b);
        String s = appender.append(product);
        return reverser.reverse(s);
    }
}
Advertisements
Categories: Java, Testing
  1. Divya
    7 April 2009 at 10:17 am

    I very much enjoyed reading this article . it was simple and to the poitn.But I wish you would have included how things work if one were to extend MockObjectTestCase.

  2. 2 July 2009 at 8:03 am

    Hi Panos,

    Your article was amazing … Thank u for elucidating this concept in such simple words… I think this article is a perfect start to JMock.

    Cheers
    Radhika Ganesan

  3. SonyThomas
    8 December 2009 at 10:19 pm

    Hi Panos,

    I am quite new to Jmock and I was struggling to understand jmock concept. But your article really helped me to set up the base to write testing with jmock. cheers mate

  4. sree
    23 March 2010 at 2:35 pm

    hey panos

    thanks for directing me to your tutorial .It makes sense to me now but i’m using jmock 1.x since my company uses it,so i’m not sure if there is an assertIsSatisfied available for use.Can you let me know.

  5. 23 March 2010 at 3:12 pm

    Hello Sree, I am not sure but I do not think so. Is there any specific reason why you can’t upgrade to JMock 2?

  6. Jegg
    4 June 2010 at 7:08 pm

    As you said: “JMock works only with interfaces (no abstract or concrete classes) so you need to have the interfaces ready before you go on testing”

    We now may use class like: “ClassImposteriser” to mock concret or abstract class….

  7. 5 June 2010 at 8:38 am

    Hello Jegg, yes you are right, we can use a ClassImposteriser to mock concrete o abstract classes. I should update my post (or post a new one) that explains how to do it. Thanks for mentioning it.

  8. paddy
    16 December 2011 at 4:50 pm

    Thanks a lot for writing this article. It helped me to understand JMock concept very easily.

  9. Oche
    27 December 2011 at 3:39 pm

    Most helpful article ive found on JMock so far,,thanks

  10. Parthasarathi S
    21 June 2012 at 7:32 am

    very nice article..very simple and enriched one.thanks to author.

  11. 6 August 2016 at 4:36 pm

    Very nice article but the code divs seem to be shrinking from top to bottom ( in Google Chrome ). It would be great if you can fix this.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: