Return the same object that was given as parameter with EasyMock

Every time I have to use EasyMock, I get annoyed. For those who don’t know EasyMock, it is a mocking framework that allows you to mock certain pieces of code when doing unit testing. In my humble opinion, there are better ways to create mocks in unit test, but at my current project I’m using EasyMock.
There are 2 reason, the first is the lack of more advanced mocking tutorials on the interwebs. When I go looking for a specific solution, I never really find what I need.
The second reason is a more justified one, but still personal. I alway forget how the API of EasyMock works, it is not self explanatory, a former colleague of mine even once called it DifficultMock…
I’m not going to rant about my lack of love for this java mocking framework, instead I’m going to offer a solution.
So basically, what I was trying to do, was to capture an argument I give to a method and return the same object. We could take the JPA method EntityManager.merge(T entity) method as example. We take an unattached object and attach it. The result is our attached object. Seems releasable to include in a test.
So I want to capture my argument and return it. After playing a bit with the API I got this

EntityManager entityManager = EasyMock.createNiceMock(EntityManager.class);
final Capture<Entity> capture = new Capture<Entity>();
EasyMock.expect(entityManager.merge(EasyMock.capture(capture))).andReturn(capture.getValue());
replay(entityManager);
executeTest();

This compiled fine, but gave an error at runtime, saying

Nothing captured yet

So I’m not able to define my andReturn method, because it already tries get the value of my captured object. Instead of giving my andReturn method a value, I have to give it a function, a closure. Since Java doesn’t have closures (yet), I’m forced to use a poor mans closure.
EasyMock supplies an Interface and a method we can use here. The IAnwser interface and we replace the andReturn method by the andAnwser method.
By creating an annonymous implementation of this interface, we create a one-function class, like a normal closure.

final EntityManager entityManager = EasyMock.createNiceMock(EntityManager.class);
final Capture<Entity> capture = new Capture<Entity>();
EasyMock.expect(entityManager.merge(EasyMock.capture(capture))).andAnswer(new IAnswer<Entity>() {

    public Entity answer() throws Throwable {
        return capture.getValue();
    }
});
replay(entityManager);

Parameterized Unit Test

When you are writing unit tests, you want to test all the border cases. You can create a method for every border case, but sometimes this is a very repetitive job.

JUnit has a more pragmatic approach for these sorts of tests, called Parameterized tests. The idea is simple. You create your testcase with your test methods. You define a list of parameters that is given to the constructor and all the tests inside the testcase are executed for every parameter.

See this example. A simple class: Calculator

package be.styledideas.blog;

public class Calculator {
    public Long add(Long a, Long b){
        return a+b;
    }
}

I want to test this method with more than one parameter. I have created this parameterized unit test.

package be.styledideas.blog;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.ArrayList;
import java.util.Collection;

@RunWith(Parameterized.class)
public class CalculatorAddTestCase {
    private Long a;
    private Long b;
    private Long expected;

    public CalculatorAddTestCase(Long a, Long b, Long expected) {
        this.a = a;
        this.b = b;
        this.expected = expected;
    }

    @Test
    public void add() {
        Long actual = new Calculator().add(a, b);
        Assert.assertEquals(expected, actual);
    }

    @Parameterized.Parameters
    public static Collection<Object[]> getParameters(){
        Collection<Object[]> parameters = new ArrayList<Object[]>();
        parameters.add(new Object[]{1L, 2L, 3L});
        parameters.add(new Object[]{-1L, 2L, 1L});
        parameters.add(new Object[]{2L, 2L, 3L});
        parameters.add(new Object[]{4L, 0L, 4L});
        return parameters;
    }
}

The first thing that is different about this test is the @RunWith annotation at the top. Instead of the Junit4 runner, we use the Parameterized runner. This annotations looks for a static method that is annotated with the @Parameters annotation.
When we look closer at the getParameters method, we see that every item in the List maps to the constructor of the TestCase, so every item represents a running test. This concrete example results in equal to 4 unit tests.

Now you can be wondering what is the added value of this annotation over a list that you pass to your test. If you are just giving a test method a List with parameters, and one parameter creates a failure in your test, you will have to search for the test that is failing. When you execute it this way, any modern IDE will point which parameter is incorrect.

Extending Spring JUnit Runner

For our integration tests we had to use the development database. On that database there are some batch parameters stored and one of those parameters is a cutoffTime. As the name suggest, our business logic can not be processed after that time, so we had to create a mechanism to make sure that the tests are not executed, else the build would fail.
The first thing we did was a quick fix. We added an if-statement to the test to see what time it is and if the test can be executed. As some of you already know, an empty method annotated with @Test will execute and be successful. This is not correct, when someone breaks code that is in those tests, the test should not be marked as successful. We should be able to add the @Ignore annotation on the tests when the cutOffTime is passed.

I looked into this problem and the first thing that came to my mind was to use Annotations, because it is meta-data and not test functionality. JUnit4 has a very easy way to handle annotations, so I decided to go that way.

/**
 * The CutOffTime annotation..
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CutOffTime {
    String cutOffTime();
}

I want this annotation to cancel all my tests in a testcase, not just a method, that is why i put the ElementType on TYPE.

Our integration tests are running with the SpringJUnit4ClassRunner so I extended this. I have overridden the invokeTestMethod method, because this is the one executing each test. Here is the source code

/**
 * The Class IntegrationTestClassRunner. This class is a specific class runner
 * for the payments. There is a cutoffTime defined in the database that decides
 * if a payment that can be made or not. This can be applied by the
 * {@link CutOffTime} annotation
 *
 * @author Jelle Victoor
 * @version 14-apr-2010
 */
public class IntegrationTestClassRunner extends SpringJUnit4ClassRunner {

    /**
     * The Constructor.
     *
     * @param clazz
     *            the clazz
     *
     * @throws InitializationError
     *             the initialization error
     */
    public IntegrationTestClassRunner(final Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    /** {@inheritDoc} */
    @Override
    protected void invokeTestMethod(final Method method, final RunNotifier notifier) {
        Annotation[] classAnnotations = classAnnotations();
        Boolean ignore = Boolean.FALSE;
        for (Annotation classAnnotation : classAnnotations) {
            if (classAnnotation instanceof CutOffTime) {
                if (shouldIgnore(((CutOffTime) classAnnotation).cutOffTime())) {
                    ignore = Boolean.TRUE;
                }
                break;
            }
        }
        if (!ignore) {
            super.invokeTestMethod(method, notifier);
        } else {
            notifier.fireTestIgnored(methodDescription(method));
        }
    }

    /**
     * Decides to ignore the test
     *
     * @param cuttOffTime
     *            the cutt off time
     *
     * @return true, if should ignore
     */
    private boolean shouldIgnore(final String cuttOffTime) {
        int hour = Integer.parseInt(StringUtils.substring(cuttOffTime, 0, 2));
        int minutes = Integer.parseInt(StringUtils.substring(cuttOffTime, 2, 4));
        return new LocalTime().isAfter(new LocalTime(hour, minutes));
    }
}

As you can see, the classAnnotation() method will give you all the annotations that are on your class *and are specified with @Retention(RetentionPolicy.RUNTIME)*. I simply check if the annotation is found on the class. When I’m done checking if the annotation is found and if the test should be ignored, i can fireTestIgnored on the notifier or execute the test. We just annotate our tests with @RunWith(IntegrationTestClassRunner.class).
This way the test should not fail or succeed, but now you can simply see that the test is ignored. The statistics are clean :-).

I know that the whole purpose of this extention is odd, but we are forced into this position. With this post I’m trying to show you that extending a test framework like JUnit isn’t hard at all and you can use it in every way you want to.