5

I'm having a problem with ArgumentCaptor not being able to record the arguments when calling the same method a number of times. Basically this does not seem to work:

List<Dummy> mList = mock(List.class);
Dummy dummy = new Dummy();
when(mList.get(anyInt())).thenReturn(dummy);

Dummy d = mList.get(12);
d.setName("John");
mList.add(d);

Dummy g = mList.get(10);
g.setName("Ben");
mList.add(g);
...

verify(mymock, times(3)).doStuff(captor.capture)); 
assertEquals("John", captor.getAllValues().get(0).getName()); 
assertEquals("Ben", captor.getAllValues().get(1).getName()); 
assertEquals("Don", captor.getAllValues().get(2).getName()); 

The value of getName() is always set to "Don". I have also tried using InOrder, with the same outcome.

Feature (and me stupiud) or bug?

To better explain the issue I have created a use case: http://pastebin.com/RE1UzJ4F

Cheers

3 Answers 3

4

The java doc for ArgumentCaptor suggests what you are trying, so I'd say this is a bug. However, it is a bug in your code.

The problem is that you're changing the name of the same dummy each time you're invoking setName(..). I'd suggest that you make Dummy immutable and avoid setters wherever you can. That will avoid these types of bugs.

If you cannot make your Dummy immutable to force the issue you should at least pass a different instance from each get. Doing

when(mList.get(anyInt())).thenReturn(new Dummy(), new Dummy(), new Dummy());

Would fix the problem.

3
  • Yes, of course, the test code was just a way to explain the problem. Commented Jul 26, 2010 at 11:12
  • 2
    My point is that the only problem is in fact in your code, not in Mockito. Is that what you were trying to explain (in that case I'm sorry I missed it)?
    – iwein
    Commented Jul 27, 2010 at 16:21
  • I've fixed your sample in the pastebin (without compiling though).
    – iwein
    Commented Sep 15, 2010 at 5:43
2

iwein is correct; however, there are some situations (such as embedded systems) in which memory is scarce and you do not want to use or cannot use immutability.

A workaround I have found is to use a different mock for each invocation, then verify a list of mocks that each have one invocation.

List<Mock> mocks = new ArrayList<Mock>();
...init list w/ mocks using for loop...
List<Object[]> expectedArgs = new ArrayList<Object[]>();
..init list w/ desired args...

mocks.get(0).callMethod(1, 2);
 ...do that some more...

for(int i = 0; i < mocks.size(); i++) {
     Object[] desiredArgs = expectedArgs.get(i);
     verify(mocks.get(i)).callMethod((int) desiredArgs[0], (int) desiredArgs[1]);
 }

It is not as pretty, but you do not have to make your classes immutable this way.

1

I had this problem and ended up using atLeastOnce, like so:

private ActionRequest getRequestedAction() {
    ArgumentCaptor<ActionRequest> captor = ArgumentCaptor.forClass(ActionRequest.class);
    verify(adapter, atLeastOnce()).requestAction(captor.capture());
    return captor.getValue();
}

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.