Archive

Archive for the ‘Testing’ Category

Testing with jMock

27 May 2008 11 comments

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

Testing a stored procedure using hsqldb

5 December 2007 9 comments

Today I needed to test a data access object that will be calling a stored procedure in the database. Since we are using HSQLDB and mock objects to do all the database interactions while we develop, I needed to test it without having access to the actual stored procedure. At first I wasn’t sure how to do it. Then I started looking at the HSQLDB documentation and I realised that I could create an alias to a static method and treat it exactly like I could treat the stored procedure.

Lets say that the stored procedure returns a random number from 1 to 100. In my test class I define a static method that returns this number

public static int random()
{
return 1 + (int)(Math.random() * 100);
}

Then I create an alias to this method call


// Define the alias to the stored procedure as a
// static final variable
private static final String SP =
"CREATE ALIAS randomStoredProcedure
FOR\"my.class.DaoTester.random\"";

Note that I needed to define the full class name of my test class, including the package. I also used double quotes around the class declaration since HSQLDB automatically converts all lower case letters to upper case.

Once the stored procedure is loaded up in the in-memory database using

// Load the alias.
jdbc.update(SP);

it can be called from the actual DAO implementation class like you would call any other stored procedure


// Call it to get the random number.
int random = jdbc.queryForInt("{call randomStoredProcedure()}");

The jdbc variable is of type SimpleJdbcOperations from the Spring framework. The implementation class can be used without changes with the real stored procedure. Neat.

Categories: Hsqldb, Java, Testing