Re: [mockito] Verify multiple calls to the same method with reused object

8,430 views
Skip to first unread message

Eric Lefevre-Ardant

unread,
Apr 2, 2013, 12:19:27 PM4/2/13
to moc...@googlegroups.com

On 2 April 2013 12:10, Alexander Broekhuis <a.bro...@gmail.com> wrote:
How can I get mockito to record the values, and not only the object reference?

I don't think you can.

TBH, I feel this is rather a good thing. In general, you should not reuse a modified data object between two external calls. The problem being that you are never sure what the external system will do. For example, if the call is asynchronous, then it has a good chance to to its job with the wrong version of the data.

This sounds like a case where the tests might force you to make your code more resilient, for example by making your Data objects immutable.

Alexander Broekhuis

unread,
Apr 4, 2013, 2:46:52 AM4/4/13
to moc...@googlegroups.com
Thanks for your reply,

Op dinsdag 2 april 2013 18:19:27 UTC+2 schreef Eric Lefevre-Ardant het volgende:

On 2 April 2013 12:10, Alexander Broekhuis <a.bro...@gmail.com> wrote:
How can I get mockito to record the values, and not only the object reference?

I don't think you can.

<snip> 
 

This sounds like a case where the tests might force you to make your code more resilient, for example by making your Data objects immutable.

While I tend to agree with you, I also think this might be wrong in some cases. For us this is a piece of code which handles hardware messages. For performance reasons it is quite common to use pools for messages. Before sending, retrieve a message from the pool, after sending, return it to the pool.
When the message is being sent, the object is actually immutable, but since the sending happens within the unit to test, for the entire unit, the object is cleared and returned to the pool before exiting the method. My use case above was a very simplified version, but that model is also used for performance reasons on restricted devices where object creation is quite expensive...
So while the provided example explicitly reuses the object, the problem is still present with pools. The object is returned and cleaned after usage (which is before exiting the method).

I'd expect some sort of feature in a testing framework where I can tailor the mock to handle arguments on a custom way. For example by creating a clone instead of simply recording the reference.

David Wallace

unread,
Apr 4, 2013, 2:58:57 AM4/4/13
to moc...@googlegroups.com
Use an argument captor, and check at the end of the tests that the values in it are what you were expecting.  So in your case, this might look like this.

@Captor private ArgumentCaptor<Data> captor;

...

verify(mock, times(3)).done(captor.capture());
assertEquals(Arrays.asList(new Data("a"),new Data("b"),new Data("c")), captor.getAllValues());



On Tue, Apr 2, 2013 at 11:10 PM, Alexander Broekhuis <a.bro...@gmail.com> wrote:
Hi all,

I have some existing code for which I am writing tests. In this code a pool of objects is used, and when calling a certain method on a mock, the argument is an entry for this pool. If the size of the pool is 1 (one). Only one object is used for each call.
When I try to verify 3 calls with different properties placed in the argument, they only verify if I test against the last set of properties.
In other words, since all calls use the same object (with different content), the mock doesn't record the previous calls. (it probably does, but since the value is overwritten the actual goal isn't achieved). 

How can I get mockito to record the values, and not only the object reference?

TiA!

Pseudo code without a pool, but with object reuse:

public void test() {

  Sub mock = mock(Sub.class);
  ToTest obj = new ToTest(mock);

  obj.doo();
  
  // The first two are not valid
  verify(mock).done(new Data("a")):
  verify(mock).done(new Data("b")):
  // The mock expects this one three times
  verify(mock).done(new Data("c")):
}

ToTest {

  Sub m_sub;

  ....

  void doo() {
    Data data = new Data();
    doo(data, "a");
    doo(data, "b");
    doo(data, "c");
  }

  void doo(Data data, String val) {
    data.setValue(val);
    m_sub.done(data);
  }
}

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

Alexander Broekhuis

unread,
Apr 4, 2013, 3:19:03 AM4/4/13
to moc...@googlegroups.com
Hi David,

Thanks for your reply!

2013/4/4 David Wallace <dmwall...@gmail.com>

Use an argument captor, and check at the end of the tests that the values in it are what you were expecting.  So in your case, this might look like this.

Maybe I don't get Captor completely.. But how does this help me? The captor still only records the reference, and the getAllValues gives me back these references.

Is there a way to interact with the captured argument before it is recorded? Then I could create a copy of it.
 

@Captor private ArgumentCaptor<Data> captor;

...

verify(mock, times(3)).done(captor.capture());
assertEquals(Arrays.asList(new Data("a"),new Data("b"),new Data("c")), captor.getAllValues());

In my case the problem is that all captured values point to the same reference or a cleaned up reference if it is put back into a pool. So either the expected argument is already overwritten if Data is mutable, or it is cleaned by the pool.
 

--
You received this message because you are subscribed to a topic in the Google Groups "mockito" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mockito/w95iWT66Jfo/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to mockito+u...@googlegroups.com.

To post to this group, send email to moc...@googlegroups.com.
Visit this group at http://groups.google.com/group/mockito?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Met vriendelijke groet,

Alexander Broekhuis

Eric Lefevre-Ardant

unread,
Apr 4, 2013, 3:45:10 AM4/4/13
to moc...@googlegroups.com
I might be reading too much into what you are writing, but does this mean that you do not own the Sub and Data classes?
Because if you do, there must be way to refactor them in order for Sub.done() to take a new Data instance every time, and then wrap it or copy it into a reused instance coming of something else coming from a pool of instances.

If you do not own those classes, maybe this is a sign that you should wrap them with classes you own, à la Growing Object Oriented Software (GOOS).
Those new classes might reveal to be so simple they do not need to be unit tested. Or maybe they are easier to test.

Contrary to the argument in GOOS, I tend to often refer directly to my external services (ie. classes that I do not control) in my tests. However, issues like the one you describe is a sign that I need to wrap them.

PS: David's suggestion with the argument captor is worth trying, but I agree that it probably stores references, so it will probably not work


Alexander Broekhuis

unread,
Apr 4, 2013, 3:54:50 AM4/4/13
to moc...@googlegroups.com
Hi Eric,

Thanks for thinking along,

2013/4/4 Eric Lefevre-Ardant <er...@ericlefevre.net>

I might be reading too much into what you are writing, but does this mean that you do not own the Sub and Data classes?
Because if you do, there must be way to refactor them in order for Sub.done() to take a new Data instance every time, and then wrap it or copy it into a reused instance coming of something else coming from a pool of instances.

I do own the code, but the entire point of the pools and mutable objects is to prevent object creation. If this would be used on less restricted hardware, refactoring or wrapping wouldn't be a problem.
But in this case I explicitly don't want this...
 

If you do not own those classes, maybe this is a sign that you should wrap them with classes you own, à la Growing Object Oriented Software (GOOS).
Those new classes might reveal to be so simple they do not need to be unit tested. Or maybe they are easier to test.

Contrary to the argument in GOOS, I tend to often refer directly to my external services (ie. classes that I do not control) in my tests. However, issues like the one you describe is a sign that I need to wrap them.

See my above argument.. Sadly there are other constraints preventing me to do this at this moment. I could solve the pool problem by providing a different implementation for testing. In this implementation, instead of reusing object, I could create a new instance every time.
Since tests aren't run on the restricted hardware, this is no problem (even if tests need to be run on this hardware, the goal is to test functionality, not performance). But this doesn't solve the mutable objects problem, and that model is also used quite often.
 

PS: David's suggestion with the argument captor is worth trying, but I agree that it probably stores references, so it will probably not work

I tried this and it indeed doesn't work..
 


On 4 April 2013 08:46, Alexander Broekhuis <a.bro...@gmail.com> wrote:
Thanks for your reply,

Op dinsdag 2 april 2013 18:19:27 UTC+2 schreef Eric Lefevre-Ardant het volgende:

On 2 April 2013 12:10, Alexander Broekhuis <a.bro...@gmail.com> wrote:
How can I get mockito to record the values, and not only the object reference?

I don't think you can.

<snip> 
 

This sounds like a case where the tests might force you to make your code more resilient, for example by making your Data objects immutable.

While I tend to agree with you, I also think this might be wrong in some cases. For us this is a piece of code which handles hardware messages. For performance reasons it is quite common to use pools for messages. Before sending, retrieve a message from the pool, after sending, return it to the pool.
When the message is being sent, the object is actually immutable, but since the sending happens within the unit to test, for the entire unit, the object is cleared and returned to the pool before exiting the method. My use case above was a very simplified version, but that model is also used for performance reasons on restricted devices where object creation is quite expensive...
So while the provided example explicitly reuses the object, the problem is still present with pools. The object is returned and cleaned after usage (which is before exiting the method).

I'd expect some sort of feature in a testing framework where I can tailor the mock to handle arguments on a custom way. For example by creating a clone instead of simply recording the reference.

--
You received this message because you are subscribed to the Google Groups "mockito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mockito+u...@googlegroups.com.

To post to this group, send email to moc...@googlegroups.com.
Visit this group at http://groups.google.com/group/mockito?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

--
You received this message because you are subscribed to a topic in the Google Groups "mockito" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mockito/w95iWT66Jfo/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to mockito+u...@googlegroups.com.

To post to this group, send email to moc...@googlegroups.com.
Visit this group at http://groups.google.com/group/mockito?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

David Wallace

unread,
Apr 4, 2013, 3:59:57 AM4/4/13
to moc...@googlegroups.com
Oh, I see.  I misunderstood your issue.

What you need to do is to use an Answer object, that copies the arguments, and then run some assertions on it later.  Look up Answers in the Mockito documentation; if you can't work it out, then email me and I'll help you more.


Eric Lefevre-Ardant

unread,
Apr 4, 2013, 4:40:24 AM4/4/13
to moc...@googlegroups.com

How about you refactor your code in this way?

ToTest {

  SubWrapper m_sub;

  void doo() {
    m_sub.done("a");
    m_sub.done("b");
    m_sub.done("c");
  }
}

SubWrapper {
  Sub m_sub;
  Data data = new Data();

  void done(String s) {
    data.setValue(s);
    m_sub.done(data);
  }
}

It might not seem much, but I believe that it makes it vastly easier to test the ToTest class.
Then, testing SubWrapper (ugly name, you'll need to find the right one) is probably a matter of injecting a mock Data instance from your test in a special constructor.

What do you think?

Tom Verelst

unread,
Apr 4, 2013, 3:31:28 AM4/4/13
to moc...@googlegroups.com
Another possibility is to use InOrder.

InOrder inOrder = inOrder(mock);
inOrder.verify(mock).done(new Data("a"));
inOrder.verify(mock).done(new Data("b"));
inOrder.verify(mock).done(new Data("c"));

Alexander Broekhuis

unread,
Apr 4, 2013, 5:54:29 AM4/4/13
to moc...@googlegroups.com
Hi,

2013/4/4 Eric Lefevre-Ardant <er...@ericlefevre.net>


How about you refactor your code in this way?
 
<snip>
 
It might not seem much, but I believe that it makes it vastly easier to test the ToTest class.
Then, testing SubWrapper (ugly name, you'll need to find the right one) is probably a matter of injecting a mock Data instance from your test in a special constructor.

What do you think?


I like this idea, and if the underlying code was simple enough I could use it. But there is some more in there which makes it tricky to change. I tried David his idea using Answers, and that seems to work. So I'll go for that one for now.

But thanks for the ideas and help!

Alexander Broekhuis

unread,
Apr 4, 2013, 5:57:54 AM4/4/13
to moc...@googlegroups.com
Hi,

2013/4/4 David Wallace <dmwall...@gmail.com>

Oh, I see.  I misunderstood your issue.

No problem :)
 

What you need to do is to use an Answer object, that copies the arguments, and then run some assertions on it later.  Look up Answers in the Mockito documentation; if you can't work it out, then email me and I'll help you more.

This seems to work, I need to do some cleanup in my test code, but now I can record the argument myself (and create a copy if needed).

Thanks!

Alexander Broekhuis

unread,
Apr 4, 2013, 5:56:39 AM4/4/13
to moc...@googlegroups.com
Hi,

2013/4/4 Tom Verelst <tomve...@gmail.com>

Another possibility is to use InOrder.

Ordering isn't the problem, the problem is that each done is called with the same reference. So A is overwritten with B, and B with C. And the final object is B. B is referenced 3 times by the mock.

InOrder inOrder = inOrder(mock);
inOrder.verify(mock).done(new Data("a"));
inOrder.verify(mock).done(new Data("b"));
inOrder.verify(mock).done(new Data("c"));
So for this code the first verify already fails, because for the mock only C is referenced.
Reply all
Reply to author
Forward
0 new messages