Returning a method parameter in JMock

I recently ran into a problem where I needed to test a Java method that dynamically created objects inside. These objects needed to be passed to and returned by a mock service class, but I did not know how to inject this class since it was created inside the method being tested.

Here is a code snippet of the method I was trying to test. As you can see, the Subscription object is created inside the method, then passed and returned from the subscription service. The service returns the Subscription object because it calls a JPA DAO object to persist the Subscription.

public void subscribe(ShoppingCart cart) throws PaymentGatewayException {

... 

// create subscription object
Subscription subscription = new Subscription();
subscription.setProfile(invoice.getProfile());
subscription.setProductSubscription(productSubscription);
subscription.setSubscriberID(responseDetails.getProfileID());
subscription.setStatus(SubscriptionStatus.PENDING_PAYMENT);
subscription.setSource(SubscriptionSource.PAYPAL);
subscription = subscriptionService.set(subscription);

...

To test the method, I created a mock object for the subscription service (and other needed services) and called the subscribe method. The problem I ran into was that I could not find a way to tell the mock context exactly which Subscription object was being passed and returned from subscriptionService.set(). And to make things more difficult, I needed to perform assert checks on the Subscription object.

The following code shows my original test plan. What happens here is that the subscriptionService.set() receives the Subscribe object that is intended, but it also needs to return the same object. Returning a new instance of Subscription makes the asserts fail. I could do some kind of clone of the Subscription object, but that wouldn’t be as fun as extending JMock.


final Invoice invoice = new Invoice();

context.checking(new Expectations() {{
	one(shoppingCartService).createInvoiceFromCart(cart);
	will(returnValue(invoice));
	one(subscriptionService).set(with(any(Subscription.class)));
	will(returnValue(new Subscription()));
}});

// call the test method
service.subscribe(cart);

subscription = invoice.getSubscription();
assertEquals(SubscriptionStatus.PENDING_PAYMENT, subscription.getStatus());
assertEquals(SubscriptionSource.PAYPAL, subscription.getSource());
assertNotNull(subscription.getCreationDate());
assertNotNull(subscription.getProductSubscription());
assertNotNull(subscription.getSubscriberID());

So after some research on creating custom JMock action classes, I found a simple generic way to have a test return one of the method parameters. I changed the context expectations to the following:

context.checking(new Expectations() {{
	one(shoppingCartService).createInvoiceFromCart(cart);
	will(returnValue(invoice));
	// using custom action class to return the parameter of set
	one(subscriptionService).set(with(any(Subscription.class)));
	will(ReturnParameterAction.returnParameter(0));
}});

and now the dynamically created Subscription class is passed in to the subscription service as parameter index 0. Using the ReturnParameterAction custom class, I can now return the parameter at index 0 for the set method. I included the following ReturnParameterAction class in my codebase in a nice package for later re-use.

import org.hamcrest.Description;
import org.jmock.api.Action;
import org.jmock.api.Invocation;


/**
 * The Class ReturnParameterAction returns a parameter of
 * the method invoked.
 */
public class ReturnParameterAction implements Action {
    
    /** The index of the parameter to return. */
    private int index;
    
    /**
     * Instantiates a new return parameter action.
     * 
     * @param index the index
     */
    public ReturnParameterAction(int index) {
        this.index = index;
    }
    
    /* (non-Javadoc)
     * @see org.hamcrest.SelfDescribing#describeTo(org.hamcrest.Description)
     */
    public void describeTo(Description description) {
        description.appendText("returns parameter");
    }
    
    /* (non-Javadoc)
     * @see org.jmock.api.Invokable#invoke(org.jmock.api.Invocation)
     */
    public Object invoke(Invocation invocation) throws Throwable {
        return invocation.getParameter(index);
    }


    /**
     * Static method used to instantiate this action. 
     * 
     * @param index the index
     * 
     * @return the action
     */
    public static  Action returnParameter(int index) {
        return new ReturnParameterAction(index);
    }
}

Enjoy!

Leave a Reply

Your email address will not be published. Required fields are marked *