Problem with ArgumentCaptor and consecutive call to same methods (bug or feature?)

9,192 views
Skip to first unread message

Luciano Fiandesio

unread,
Jul 23, 2010, 2:23:10 PM7/23/10
to mockito
Hello,

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:

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/YH0iAq1P

Cheers
Luciano

szczepiq

unread,
Jul 24, 2010, 2:03:46 PM7/24/10
to moc...@googlegroups.com
Hey,

Code from pastebin:

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

Please extract a local variable from 'new Dummy()' and you will immediately understand what's going on.

Cheers,
Szczepan


--
You received this message because you are subscribed to the Google Groups "mockito" group.
To post to this group, send email to moc...@googlegroups.com.
To unsubscribe from this group, send email to mockito+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mockito?hl=en.


Luciano Fiandesio

unread,
Jul 26, 2010, 4:48:11 AM7/26/10
to mockito
Hi,
I have tried to extract the variable, as suggested, but no difference

New code:
http://pastebin.com/ynwjbsat

I'm not sure I understand what is the difference in extracting the
variable in the context of this test.

Luciano
> > mockito+u...@googlegroups.com<mockito%2Bunsu...@googlegroups.com >
> > .

szczepiq

unread,
Jul 26, 2010, 5:03:12 AM7/26/10
to moc...@googlegroups.com
>I'm not sure I understand what is the difference in extracting the
variable in the context of this test.

No difference, really. I thought it is going to be more obvious that you return the same instance all the time. Hence you change the same instance every time. Therefore captured args are the same. Makes sense?

Cheers,
Szczepan

To unsubscribe from this group, send email to mockito+u...@googlegroups.com.

Luciano Fiandesio

unread,
Jul 26, 2010, 5:26:18 AM7/26/10
to mockito
Yes, it does...but still the attached test is failing.
In the context of the test, doesn't really matter if the returned
object is new or instantiated only once.
Is Mockito behaving like that by default?

Luciano
> > <mockito%2Bunsu...@googlegroups.com<mockito%252Bunsubscribe@googlegroup s.com>>

Max

unread,
Jul 26, 2010, 6:08:09 AM7/26/10
to moc...@googlegroups.com
This is not the Mockito behaviour but the behaviour of Java. In the context of the test or even any context, it does matter if the returned object is new or instantiated only once.

If you change the same object three times before verifying values the object will have whatever last value you set. So


assertEquals("John", captor.getAllValues().get(0).getName());
assertEquals("Ben", captor.getAllValues().get(1).getName());
assertEquals("Don", captor.getAllValues().get(2).getName())
;

is the same as

assertEquals("John", "Don");
assertEquals("Ben", "Don");
assertEquals("Don", "Don");

If you want your test pass then do,
  1.                 Dummy d = mList.get(12);
  2.                 d.setName("John");
  3.                 d.setId(100);
  4.                 mList.add(d);
  5.                 assertEquals("John", captor.getAllValues().get(0).getName());

  6.                 Dummy g = mList.get(10);
  7.                 mList.get(12);
  8.                 g.setName("Ben");
  9.                 g.setId(200);
  10.                 mList.add(g);
  11.                 assertEquals("Ben", captor.getAllValues().get(1).getName());

  12.                 Dummy x = mList.get(120);
  13.                 x.setName("Don");
  14.                 x.setId(300);
  15.                 mList.add(x);
  16.                 assertEquals("Don", captor.getAllValues().get(2).getName());
P.S. I think you're confused by when(mList.get(anyInt())).thenReturn(new Dummy()); This does not say when mList.get() is called return a new object, it says when mList.get() is called return this object.


To unsubscribe from this group, send email to mockito+u...@googlegroups.com.

Luciano Fiandesio

unread,
Jul 26, 2010, 6:58:28 AM7/26/10
to mockito
Ok, I think I misunderstood the usage of the ArgumentCapture.
I thought the ArgumentCapture was able to record the state of whatever
object used as argument, no matter if new or pre-instantiated.
But then, my question is, how do I test that something like that:

Code:
myservice.doSomething("a");
myservice.doSomething("b");
myservice.doSomething("c");

Test:
Test that the method "doSomething" has been called 3 times with
arguments "a", "b", "c"

Is it only doable using custom ArgumentMatcher? Like in this revised
test case:
http://pastebin.com/2c3zF8sv

Luciano



On Jul 26, 12:08 pm, Max <maxon...@gmail.com> wrote:
> This is not the Mockito behaviour but the behaviour of Java. In the context
> of the test or even any context, it does matter if the returned object is
> new or instantiated only once.
>
> If you change the same object three times before verifying values the object
> will have whatever last value you set. So
>
> assertEquals("John", captor.getAllValues().get(0).getName());
> assertEquals("Ben", captor.getAllValues().get(1).getName());
> assertEquals("Don", captor.getAllValues().get(2).getName());
>
> is the same as
>
> assertEquals("John", "Don");
> assertEquals("Ben", "Don");
> assertEquals("Don", "Don");
>
> If you want your test pass then do,
>
>    1.                 Dummy d = mList.get(12);
>    2.                 d.setName("John");
>    3.                 d.setId(100);
>    4.                 mList.add(d);
>    5.                 assertEquals("John", captor.getAllValues().get(0).
>    getName());
>    6.
>    7.                 Dummy g = mList.get(10);
>    8.                 mList.get(12);
>    9.                 g.setName("Ben");
>    10.                 g.setId(200);
>    11.                 mList.add(g);
>    12.                 assertEquals("Ben", captor.getAllValues().get(1).
>    getName());
>    13.
>    14.                 Dummy x = mList.get(120);
>    15.                 x.setName("Don");
>    16.                 x.setId(300);
>    17.                 mList.add(x);
>    18.                 assertEquals("Don", captor.getAllValues().get(2).
>    getName());
> > > > <mockito%2Bunsu...@googlegroups.com<mockito%252Bunsubscribe@googlegroup s.com>

Carsten Behring

unread,
Aug 5, 2010, 5:00:15 PM8/5/10
to mockito
I had a very similar problem. I had some production code, like this:

Hashmap map = new HashMap();
map.put("key", "value")
service.doSomething(map);
map.clear()

I wanted to test, if the map pasted to doSomething is correct.

Testing this with mockito and using a captor has the problem that at
the moment, in which I "verify", the parameter "map" is always
cleared.
So how can I write a test with mockito, which just tests that the
method "service" is called with the correct map.

Maybe the problem is, that a Mockito mock can not be setup with
"expectations" as in other mock frameworks

On Jul 26, 12:58 pm, Luciano Fiandesio <i...@lucianofiandesio.com>
wrote:

szczepiq

unread,
Aug 6, 2010, 3:54:37 AM8/6/10
to moc...@googlegroups.com
For:


myservice.doSomething("a");
myservice.doSomething("b");
myservice.doSomething("c");

test can be:

verify(myservice).doSomething("a");
verify(myservice).doSomething("b");
verify(myservice).doSomething("c");

Helpful? :)
Szczepan

To unsubscribe from this group, send email to mockito+u...@googlegroups.com.

szczepiq

unread,
Aug 6, 2010, 3:55:07 AM8/6/10
to moc...@googlegroups.com
Hey


>So how can I write a test with mockito, which just tests that the
method "service" is called with the correct map.

Unfortunately, It is going to be hard without changing the design. I believe this problem should occur rarely... Usually good design means that your value objects are immutable; usually, you don't pass to collaborators objects that you mutate immediately after - it can be considered as a code smell because it may introduce subtle side effects.

Say your code does something like that:

Map map = new HashMap();

map.put("key", "value")
service.doSomething(map);
map.clear(); //clearing the map here may have a side effect of changing the state of 'service' collaborator.
map.put("key2", "value2");
serviceTwo.doSomethingElse(map);

Safer alternative is:

Map map = new HashMap();

map.put("key", "value")
service.doSomething(map);
Hashmap mapTwo = new HashMap();
mapTwo.put("key2", "value2");
serviceTwo.doSomethingElse(map);


>Maybe the problem is, that a Mockito mock can not be setup with
"expectations" as in other mock frameworks

Very true. Expect-style is better for this case. Mockito focuses on spying & stubbing and it comes with a different set of trade-offs.

Is it helpful? :)
Szczepan

To unsubscribe from this group, send email to mockito+u...@googlegroups.com.

Carsten Behring

unread,
Aug 8, 2010, 7:07:21 AM8/8/10
to mockito
I agree with tour comment about bad design and better use of immutable
parameters.
Maybe the best solution for my case would be a hand written mock and
checking the passed parameters inside the service method by hand (or
change design and use immutable parameters)

Thanks for pointing this out.

But I still think that maybe Mockito should have this feature to
verify the state of parameters "how they were at the moment the
service method was called".
That this only works (in all cases) for immutable parameters is at
least surprising. It could be maybe enough that there is an optional
"callback" to be configurable,
in which the user can hook in it's proper code. (which could then do
argument checking) Something such as:

Mockito.if(mock.service(anything()).thenDoThis(new MethodCallBack() {
public void callback(method parameters) {
if (method parameter equals something) {
fail( " ..." )
}
}

}

This would be similar, but more general, then the "partial mock"
feature, in which Mockito can delegate a call to the "original"
method.

in this case I could still use the Mock provided by Mockito and do my
proper staff for validation, without the need to create a mainly empty
proper stub class of the service



Carsten

szczepiq

unread,
Aug 10, 2010, 11:16:48 AM8/10/10
to moc...@googlegroups.com
The code snippet you suggested reminds me about our good old stubbing api;

when(mock.service()).thenAnswer(new Answer() {
   //implement the callback
});


>But I still think that maybe Mockito should have this feature to
verify the state of parameters

We were thinking about providing an option at mock creation time for it.See http://code.google.com/p/mockito/issues/detail?id=126
Cheers,
Szczepan

To unsubscribe from this group, send email to mockito+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages