Friday, May 10, 2013

jMock custom actions, doAll expectation and onConsecutiveCalls

I had an issue with a class that I've not encountered before with projects on which I use jMock: I have a mocked class whose method modifies an object it takes as a parameter.  I suspect this is due to poor coupling in the application, but I'll refactor at a later date -- for now I welcome the challenge as an opportunity to explore three aspects of jMock with which I'm unfamiliar: custom actions, doAll and onConsecutiveCalls.

Problem

public class WillBeMocked {
    public Object processBusinessObject businessObject) {
        ReturnObject returnObject = new ReturnObject(businessObject);

        // change state of input parameter
        businessObject.incrementCounter();

        return returnObject;
    }
}

Solution


create custom jmock.org.api.Action in my test class

private static class BusinessObjectIncrementCounterAction<T> implements org.jmock.api.Action
{
    private final BusinessObject businessObjectInstance;

    public BusinessObjectIncrementCounterAction(final BusinessObject businessObjectInstance)
    {
        this. businessObjectInstance = businessObjectInstance;
    }

    @Override
    public void describeTo(final Description description)
    {
        description.appendText("calls incrementCounter() on the businessObjectInstance");
    }

    @Override
    public Object invoke(final Invocation invocation) throws Throwable
    {
        businessObjectInstance.incrementCounter();
        return null;
    }
}


create static call to construct custom Action in my test class

private static <T> org.jmock.api.Action incrementCounter(final BusinessObject businessObjectInstance)
{
    return new BusinessObjectIncrementCounterAction <T>(businessObjectInstance);
}


use doAll to execute custom Action and mocked class's method call

                
context.checking(new Expectations() {
    {
        oneOf(willBeMockedInstance).process(businessObjectInstance);

        will(doAll(incrementCounter(businessObjectInstance), 
             returnValue(new ReturnObject("A"))));
    }
});



use onConsecutiveCall to 

context.checking(new Expectations() {
    {
        exactly(2).of(willBeMockedInstance).process(businessObjectInstance);

        will(onConsecutiveCalls(doAll(incrementCounter(businessObjectInstance), 
             returnValue(new ReturnObject("A")))),
             doAll(incrementCounter(businessObjectInstance), 
             returnValue(new ReturnObject("B")))));
    }
});

No comments:

Post a Comment