views:

52

answers:

3

I'm building a test, in wich i need to send question, and wait for the answer. Message passing is not the problem. In fact to figure out wich answer correspond to wich question, i use an id. My id is generated using an UUID. an i want to retrieve this id, wich is given as a parameter to a mocked object. It look like this:

  oneOf(message).setJMSCorrelationID(with(correlationId));
           inSequence(sequence);

Where correlationId is the string i'd like to keep for an other expecteation like this one:

   oneOf(session).createBrowser(with(inputChannel), 
           with("JMSType ='pong' AND JMSCorrelationId = '"+correlationId+"'"));

have you got an answer?

+1  A: 

You have to create your own actions. Here is mine:

/**
 * puts the parameter array as elements in the list
 * @param parameters A mutable list, will be cleared when the Action is invoked.
 */
public static Action captureParameters(final List<Object> parameters) {
    return new CustomAction("captures parameters") {
        public Object invoke(Invocation invocation) throws Throwable {
            parameters.clear();
            parameters.addAll(Arrays.asList(invocation.getParametersAsArray()));
            return null;
        }
    };
}

You then use it like this (with a static import):

    final List<Object> parameters = new ArrayList<Object>();
    final SomeInterface services = context.mock(SomeInterface.class);
    context.checking(new Expectations() {{
        oneOf(services).createNew(with(6420), with(aNonNull(TransactionAttributes.class)));
            will(doAll(captureParameters(parameters), returnValue(true)));
    }});

To do what you want, you have to implement your own matcher. This is what I hacked up (some null checking left out, and of course I just use well known interfaces for the sample):

 @RunWith(JMock.class)
 public class Scrap {

private Mockery context = new JUnit4Mockery();

@Test
public void testCaptureParameters() throws Exception {
    final CharSequence mock = context.mock(CharSequence.class);
    final ResultSet rs  = context.mock(ResultSet.class);
    final List<Object> parameters = new ArrayList<Object>();
    context.checking(new Expectations(){{
        oneOf(mock).charAt(10);
            will(doAll(JMockActions.captureParameters(parameters), returnValue((char) 0)));
        oneOf(rs).getInt(with(new ParameterMatcher<Integer>(parameters, 0)));
    }});

    mock.charAt(10);
    rs.getInt(10);
}

private static class ParameterMatcher<T> extends BaseMatcher<T> {
    private List<?> parameters;
    private int index;

    private ParameterMatcher(List<?> parameters, int index) {
        this.parameters = parameters;
        this.index = index;
    }

    public boolean matches(Object item) {
        return item.equals(parameters.get(index));
    }

    public void describeTo(Description description) {
        description.appendValue(parameters.get(index));
    }
}
}
Yishai
with this technique it seem like i can't use the content of parameters immediatly after , in the same expectation definition
BenZen
@BenZen, I never tried it, but I can't imagine why it wouldn't work. I'll give it a try.
Yishai
+1  A: 

i found out an other solution on this site http://www.symphonious.net/2010/03/09/returning-parameters-in-jmock-2/

import org.hamcrest.*;
 import org.jmock.api.*;

 public class CapturingMatcher<T> extends BaseMatcher<T> implements Action {
public T captured;

public boolean matches(Object o) {
    try {
        captured = (T)o;
        return true;
    } catch (ClassCastException e) {
        return false;
    }
}

public void describeTo(Description description) {
    description.appendText("captured value ");
    description.appendValue(captured);
}

public Object invoke(Invocation invocation) throws Throwable {
    return captured;
}
}

It can then be used like:

 context.checking(new Expectations() {{
CapturingMatcher<String> returnCapturedValue = new CapturingMatcher<String>();
allowing(mockObject).getParameter(with(equal("expectedParameterName")), with(returnCapturedValue)); will(returnCapturedValue);
}});
BenZen
Not bad, although the author does not understand casting in the context of generics (the ClassCastException cannot be thrown due to type erasure unless T was declared to extend something). The disadvantage of this solution is that you do not actually capture the parameter for later use in the test, if that matters.
Yishai
in fact this solution help only if you immediatly use the captured valueyour solution is goodi mise used it, sorry
BenZen
A: 

One more option to consider, where does the correlation ID come from? Should that activity be injected in so that you can control it and check for it in the test?

Steve Freeman
i m working on an old j2se programso i can't use dependency injectioni could use manual injection but that will look wierd.But in general you id is good
BenZen