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")))));
    }
});

Wednesday, May 8, 2013

Return code is: 401, ReasonPhrase:Unauthorized. -> [Help 1]: Nexus error via Maven via Jenkins when using a slave node: Solved


[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project project: Failed to deploy artifacts: Could not transfer artifact com.company:project:jar:build_id from/to dirname (http://server:port/nexus/content/repositories/snapshots): Failed to transfer file: http://server:port/nexus/content/repositories/snapshots/com/company/product/build_id/product-build_id.jar. Return code is: 401, ReasonPhrase:Unauthorized. -> [Help 1]

(Output modified to protect the innocent.)

I have Maven builds running on a Jenkins master with slave nodes.  The deploy goal attempts to push artifacts from the build out to our Nexus instance.

To address this issue I had to add settings.xml to the Jenkins slave owner's .m2 directory as this is used during the upload to Nexus, not the settings.xml in the .m2 directory on the Jenkins master box.