32

public Object doSomething(Object o); which I want to mock. It should just return its parameter. I tried:

Capture<Object> copyCaptcher = new Capture<Object>();
expect(mock.doSomething(capture(copyCaptcher)))
        .andReturn(copyCatcher.getValue());

but without success, I get just an AssertionError as java.lang.AssertionError: Nothing captured yet. Any ideas?

Jitesh Upadhyay
  • 5,244
  • 2
  • 25
  • 43
Jan
  • 2,803
  • 6
  • 36
  • 57

5 Answers5

30

Well, the easiest way would be to just use the Capture in the IAnswer implementation... when doing this inline you have to declare it final of course.

MyService mock = createMock(MyService.class);

final Capture<ParamAndReturnType> myCapture = new Capture<ParamAndReturnType>();
expect(mock.someMethod(capture(myCapture))).andAnswer(
    new IAnswer<ParamAndReturnType>() {
        @Override
        public ParamAndReturnType answer() throws Throwable {
            return myCapture.getValue();
        }
    }
);
replay(mock)

This is probably the most exact way, without relying on some context information. This does the trick for me every time.

does_the_trick
  • 301
  • 3
  • 3
  • I like the post from Remi Fouilloux more and use it very often. It removes the need for a catpure object. – Jan Oct 26 '11 at 15:39
  • 3
    Great answer. Using Java 8 lambda's the entire IAnswer anonymous subclass can be rewritten as "myCapture::getValue". – Henno Vermeulen Feb 16 '17 at 13:47
19

I was looking for the same behavior, and finally wrote the following :

import org.easymock.EasyMock;
import org.easymock.IAnswer;

/**
 * Enable a Captured argument to be answered to an Expectation.
 * For example, the Factory interface defines the following
 * <pre>
 *  CharSequence encode(final CharSequence data);
 * </pre>
 * For test purpose, we don't need to implement this method, thus it should be mocked.
 * <pre>
 * final Factory factory = mocks.createMock("factory", Factory.class);
 * final ArgumentAnswer<CharSequence> parrot = new ArgumentAnswer<CharSequence>();
 * EasyMock.expect(factory.encode(EasyMock.capture(new Capture<CharSequence>()))).andAnswer(parrot).anyTimes();
 * </pre>
 * Created on 22 juin 2010.
 * @author Remi Fouilloux
 *
 */
public class ArgumentAnswer<T> implements IAnswer<T> {

    private final int argumentOffset;

    public ArgumentAnswer() {
        this(0);
    }

    public ArgumentAnswer(int offset) {
        this.argumentOffset = offset;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T answer() throws Throwable {
        final Object[] args = EasyMock.getCurrentArguments();
        if (args.length < (argumentOffset + 1)) {
            throw new IllegalArgumentException("There is no argument at offset " + argumentOffset);
        }
        return (T) args[argumentOffset];
    }

}

I wrote a quick "how to" in the javadoc of the class.

Hope this helps.

thetoolman
  • 2,144
  • 19
  • 11
rems
  • 222
  • 2
  • 4
  • thanks! Although I changed the original unit test I'm sure I'll run into this issue again! (You maybe want to contribute it to EM direclty?) – Jan Jun 24 '10 at 16:27
  • 4
    The Capture is a red herring in your javadoc example - it is not needed: EasyMock.expect(factory.encode(anyObject())).andAnswer(parrot).anyTimes(); – thetoolman Jan 17 '12 at 20:09
  • Unfortunately, moderators have decided that an edit to remove the misleading & unused `Capture` is "either completely superfluous or actively harms readability" and that it does not "strive to preserve the goals of the post's owner". – AndrewF Aug 13 '20 at 22:17
17

Captures are for testing the values passed to the mock afterwards. If you only need a mock to return a parameter (or some value calculated from the parameter), you just need to implement IAnswer.

See "Remi Fouilloux"s implementation if you want a reusable way of passing paramter X back, but ignore his use of Capture in the example.

If you just want to inline it like "does_the_trick"s example, again, the Capture is a red herring here. Here is the simplified version:

MyService mock = createMock(MyService.class);
expect(mock.someMethod(anyObject(), anyObject()).andAnswer(
    new IAnswer<ReturnType>() {
        @Override
        public ReturnType answer() throws Throwable {
            // you could do work here to return something different if you needed.
            return (ReturnType) EasyMock.getCurrentArguments()[0]; 
        }
    }
);
replay(mock)
thetoolman
  • 2,144
  • 19
  • 11
  • Not quite, my point is that in "Remi Fouilloux"s code, the Capture the javadoc example is unneeded. It is also unneeded in "does_the_trick"s example code, as improved above. – thetoolman Feb 16 '12 at 21:06
10

Based on @does_the_trick and using lambdas, you can now write the following:

MyService mock = EasyMock.createMock(MyService.class);

final Capture<ParamAndReturnType> myCapture = EasyMock.newCapture();
expect(mock.someMethod(capture(myCapture))).andAnswer(() -> myCapture.getValue());

or without capture as @thetoolman suggested

expect(mock.someMethod(capture(myCapture)))
.andAnswer(() -> (ParamAndReturnType)EasyMock.getCurrentArguments()[0]);
Marco Baumeler
  • 101
  • 2
  • 5
  • Thanks for EasyMock.newCapture(), it appears that now you cannot instantiate Capture with new as in other examples; it's a private constructor. – k-den Jun 27 '19 at 23:37
2

Um, if I understand your question correctly I think you may be over complicating it.

Object someObject = .... ;
expect(mock.doSomething(someObject)).andReturn(someObject);

Should work just fine. Remember you are supplying both the expected parameter and returne value. So using the same object in both works.

drekka
  • 20,957
  • 14
  • 79
  • 135
  • I don't know "someObject". It is instantiated in the method I want to test. Think of a method "createImage(InputStream image)" (cut) which internally calls a "Image filter(Image imge)" (mock). – Jan Apr 24 '10 at 11:39
  • Ahhh. ok. Then you can do a couple of things. Firstly you can test that the object is a particular class using the isA() argument matcher. Second I would suggest writing your own argument capture. I haven't done that, but I have written my own argument matchers. Which is really useful if you, for example, want to check bean properties. Unfortunately I don't have that code anymore, but if you look at the example of writing a matcher, it's not hard. – drekka Apr 26 '10 at 04:00
  • Your code is valid but it isn't answering the question to use one of the parameters- it is using a known object. – thetoolman Jan 13 '12 at 01:47