I'm new to Mockito, and I'm trying to use it to verify the following:
I've got class A (under test) and class B (mocked). Class A references
B, and when I call a method of class A I want to verify that certain
methods of B are called in a given sequence and with some given
parameters. I wrote the following (shortened here):
A a;
B b;
......
ArgumentCaptor<C> ac1 = ArgumentCaptor.forClass( C.class );
ArgumentCaptor<C> ac2 = ArgumentCaptor.forClass( C.class );
InOrder orderedExecution = inOrder( b );
orderedExecution.verify( b ).methodA( (String) isNull( ), (String)
isNull( ), eq( "value1" ), ac1.capture( ) );
orderedExecution.verify( b ).methodA( (String) isNull( ), (String)
isNull( ), eq( "value2" ), ac2.capture( ) );
orderedExecution.verify( b ).methodB( ... );
orderedExecution.verify( b ).methodC( ... );
orderedExecution.verify( b ).methodC( ... );
assertSomething( ac1.getValue( )... );
assertSomething( ac2.getValue( )... );
But if I do this I get a "verification in order failure" in the first
verify, due to an "undesired" invocation of methodA (was invoked
twice, but the first verify verifies that got invoked only once). I've
checked the API, and I know I can use a VerificationMode (times(2))
and then get all the values captured using ac.getAllValues()... but
what happens if the method calls are intermixed?? How should I verify
they got called in a given sequence?
Maybe I've misunderstood the InOrder verification?
Thanks in advance, best regards
José González
See also this issue:
http://code.google.com/p/mockito/issues/detail?id=168
On Mar 9, 12:37 pm, José González Gómez
--
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.
I finally found the "correct" way of testing this, but I find the
behavior of Mockito somewhat counterintuitive... to explain it I've
included a test case made with TestNG... sorry for the long post, but
I wanted to show all the posibilities:
Let's say I have a mocked object and I want to verify that some of its
methods are called in a given order with correct parameters. To
simplify I've made the following example with a mocked list, calling
the add method with different parameters with an intermixed clear
method call. I want to test that the add method is called with the
correct parameter in a given sequence, and I want to ignore any call
to any other method. I have made 5 tests to show Mockito behaviour:
Test 1, 2. They are exactly the same, they test in order execution
using hardcoded arguments... but what happens if I must capture the
arguments to make a more complex check?
Test 3. I tried to capture the arguments, but UPS!!! I can't do it the
same way as I did it with hardcoded arguments, as it seems that when I
capture arguments the verify call tries to match all the calls to the
method... so let's try another thing...
Test 4. OK, I capture the arguments using a VerificationMode
(times(2)) and then check the captured arguments, no problem...
Test 5. But then... UPS again!!! If my code no longer calls clear()
between the add() calls, the test is no longer working...
So is there any way to make in order tests with captured arguments
independent of intermixed calls to other methods?
Thanks in advance, best regards
José
public class InOrderTest
{
protected List<String> list;
@BeforeMethod
public void setUpList( )
{
this.list = (List<String>) Mockito.mock( List.class );
}
protected void exerciseListWithClear( )
{
this.list.add( "first" );
this.list.add( "second" );
this.list.clear( );
this.list.add( "third" );
}
protected void exerciseListWithoutClear( )
{
this.list.add( "first" );
this.list.add( "second" );
this.list.add( "third" );
}
@Test
public void testInOrder_intermixedClear_hardcodedArguments( )
{
this.exerciseListWithClear( );
InOrder orderedExecution = Mockito.inOrder( this.list );
orderedExecution.verify( this.list ).add( "first" );
orderedExecution.verify( this.list ).add( "second" );
orderedExecution.verify( this.list ).add( "third" );
}
@Test
public void testInOrder_noClear_hardcodedArguments( )
{
this.exerciseListWithoutClear( );
InOrder orderedExecution = Mockito.inOrder( this.list );
orderedExecution.verify( this.list ).add( "first" );
orderedExecution.verify( this.list ).add( "second" );
orderedExecution.verify( this.list ).add( "third" );
}
@Test(expectedExceptions = {VerificationInOrderFailure.class})
public void
testInOrder_intermixedClear_sequentiallyCapturedArguments( )
{
this.exerciseListWithClear( );
// verify
ArgumentCaptor<String> ac =
ArgumentCaptor.forClass( String.class );
InOrder orderedExecution = Mockito.inOrder( this.list );
orderedExecution.verify( this.list ).add( ac.capture( ) );
Assert.assertEquals( ac.getValue( ), "first" );
orderedExecution.verify( this.list ).add( ac.capture( ) );
Assert.assertEquals( ac.getValue( ), "second" );
orderedExecution.verify( this.list ).add( ac.capture( ) );
Assert.assertEquals( ac.getValue( ), "third" );
}
@Test
public void testInOrder_intermixedClear_capturedArguments( )
{
this.exerciseListWithClear( );
// verify
ArgumentCaptor<String> ac =
ArgumentCaptor.forClass( String.class );
InOrder orderedExecution = Mockito.inOrder( this.list );
orderedExecution.verify( this.list,
Mockito.times( 2 ) ).add( ac.capture( ) );
Assert.assertEquals( ac.getAllValues( ).get( 0 ), "first" );
Assert.assertEquals( ac.getAllValues( ).get( 1 ), "second" );
orderedExecution.verify( this.list ).add( ac.capture( ) );
Assert.assertEquals( ac.getValue( ), "third" );
}
@Test(expectedExceptions = {VerificationInOrderFailure.class})
public void testInOrder_noClear_capturedArguments( )
{
this.exerciseListWithoutClear( );
// verify
ArgumentCaptor<String> ac =
ArgumentCaptor.forClass( String.class );
InOrder orderedExecution = Mockito.inOrder( this.list );
orderedExecution.verify( this.list,
Mockito.times( 2 ) ).add( ac.capture( ) );
Assert.assertEquals( ac.getAllValues( ).get( 0 ), "first" );
Assert.assertEquals( ac.getAllValues( ).get( 1 ), "second" );
orderedExecution.verify( this.list ).add( ac.capture( ) );
Assert.assertEquals( ac.getValue( ), "third" );
}
}
On 9 mar, 20:46, szczepiq <szcze...@gmail.com> wrote:
> Hey,
>
> You understand the API correctly.
>
> >what happens if the method calls are intermixed?? How should I verify
>
> they got called in a given sequence?
>
> What do you mean 'intermixed'? Can you write little code?
>
> @Kristofer
>
> verify(mock).foo() == verify(mock, times(1)).foo()
>
> Hope that helps!
> Szczepan Faber
>
> 2010/3/9 José González Gómez <jose.gonzalez.go...@gmail.com>
> > mockito+u...@googlegroups.com<mockito%2Bunsu...@googlegroups.com>
To unsubscribe from this group, send email to mockito+u...@googlegroups.com.
Another example... The following test also fails, and I find it
extremely counterintuitive:
@Test(expectedExceptions = {VerificationInOrderFailure.class})
public void
testInOrder_intermixedClear_capturedArgumentsAndCheckClear( )
{
this.exerciseListWithClear( );
// verify
ArgumentCaptor<String> ac =
ArgumentCaptor.forClass( String.class );
InOrder orderedExecution = Mockito.inOrder( this.list );
orderedExecution.verify( this.list,
Mockito.atLeast( 1 ) ).add( ac.capture( ) );
Assert.assertEquals( ac.getAllValues( ).get( 0 ), "first" );
orderedExecution.verify( this.list ).clear( );
orderedExecution.verify( this.list,
Mockito.atLeast( 1 ) ).add( ac.capture( ) );
Assert.assertEquals( ac.getAllValues( ).get( 0 ), "third" );
}
Reading this code without knowing anything about the implementation,
this is what I expect it to check:
1. add is called at least 1 time, and the first time is called we use
"first" as argument
2. Then clear is called
3. Then add is called again at least 1 time, and the first time is
called after the clear call we use "third" as argument
If you compare these checks in plain english with the code exercised,
it seems the test should pass, but it fails due to greedy evaluation:
the first verify matches all of the calls to add, and then the verify
of the clear call fails... how would you test such a situation?
BTW, I have read the wiki page, and some of the examples you show as
contradicting are valid for me, so maybe we have different concepts of
"contradicting" :o)
Best regards
José
On 12 mar, 00:12, szczepiq <szcze...@gmail.com> wrote:
> Yo,
>
> I just added a page that describes why Mockito InOrder mode is greedy:http://code.google.com/p/mockito/wiki/GreedyAlgorithmOfVerficationInO...
>
> I mention that here because this is related to the issue here (well spotted
> Kristofer). Greedy or non-greedy both have pros & cons. Mockito is greedy
> because IMBO it's better :)
>
> It feels the problem should be simpler. If you have to use ArgumentCaptor
> for arguments why do you bother verifying in order? Captor contains ordered
> list of args so your assertions will prove the order.
>
> Cheers,
> Szczepan Faber
>
> On Thu, Mar 11, 2010 at 10:25 PM, Kristofer Karlsson <
>
> > On Thu, Mar 11, 2010 at 9:53 PM, szczepiq <szcze...@gmail.com> wrote:
>
> >> I get your point.
>
> >> >So is there any way to make in order tests with captured arguments
> >> >independent of intermixed calls to other methods?
>
> >> I guess you cannot do it. However, you can try writing your own argument
> >> matchers: this way you have complex argument verification + sequencing.
>
> >> Cheers,
> >> Szczepan Faber
>
> >> 2010/3/11 José González Gómez <jose.gonzalez.go...@gmail.com>
> ...
>
> leer más »
--
Regarding the bugs you mention in the wiki, as long as I read the
VerificationMethod in the following way:
* times( n ) -> something happens *exactly* n times (taking this into
account, I think it doesn't make sense to talk about greediness
here... what do you think?)
* atLeast( n ) -> something happens *at least* n times
* atMost( n ) -> something happens *at most* n times
when the second saveEntity method called is introduced, the tests
would fail... I don't know if this implies having greedy or non-greedy
evaluation... anyway I have the feeling that having greedy evaluation
leads to "counterintuitive" tests that may fail even if the behavior
in the exercised code and the expected behavior from "reading" the
tests seems to be the same. I'm not saying that non-greedy evaluation
is the solution, after all, I have no experience with it and haven't
thought about the implications...
Best regards
José
On 14 mar, 16:41, szczepiq <szcze...@gmail.com> wrote:
> If this is real example then I would not used mocks... I would just assert
> on the contents of the list giving me simpler & easier to maintain test.
>
> By 'real example' I mean a use case from production code.
>
> >I think we deserve to see a realistic use case for having it greedy as well
>
> :)
>
> That's a fair point but I don't have any. Mockito is greedy to avoid bugs
> and this is our design decision. I do appreciate your feedback but I don't
> see a point of changing the algorithm unless we see a *real* use case. I
> updated the wiki - now it shows the theoretical example where non-greedy
> algorithm leads to bugs:http://code.google.com/p/mockito/wiki/GreedyAlgorithmOfVerficationInO...
>
> >By the way, is times() even greedy at all? It seemed non-greedy when I
>
> tried it.
>
> Yes it is.
>
> Cheers,
> Szczepan
>
> On Fri, Mar 12, 2010 at 1:24 PM, Kristofer Karlsson <
>
> > On Fri, Mar 12, 2010 at 11:55 AM, szczepiq <szcze...@gmail.com> wrote:
>
> >> >Another example... The following test also fails, and I find it
> >> >extremely counterintuitive:
>
> >> Can you provide a 'real' example? I think we are not making progress on
> >> investigating theoretical cases :)
>
> >> >how would you test such a situation?
>
> >> Provide the real example and I will show you.
>
> >> >BTW, I have read the wiki page, and some of the examples you show as
> >> >contradicting are valid for me, so maybe we have different concepts of
> >> >"contradicting" :o)
>
> >> It is entirely possible we have a different concept of "contradiction"
> >> because words are simply imprecise :) Here's what I consider contradiction:
>
> >> //pass:
> >> verify(mock, times(1)).foo();
> >> //pass?
> >> verify(mock, times(2)).foo();
>
> >> With non-greedy algorithm you will not be able to perform certain
> >> verifications because times(1) will always pass.
>
> >> Cheers,
> >> Szczepan
>
> >> 2010/3/12 José González Gómez <jose.gonzalez.go...@gmail.com>
> ...
>
> leer más »
> ...
>
> leer más »
HeyI don't know :) what you think? http://pastebin.com/ Not knowing your code I have a feeling that it might be better to test it without mocks (eg. generate xml and compare the content by string or with some tool like xml unit).
>I'm completely
>open to suggestions on how to improve my testing abilities :o)
Why not? Your initial post was the case with times(1)
>* times( n ) -> something happens *exactly* n times (taking this into
>account, I think it doesn't make sense to talk about greediness
>here... what do you think?)I agree. Greedy algorithm seems counter intuitive. However it prevents from bugs and is consistent with how Mockito works. I did some final amendments to the wiki, hopefully it is clear now :)
>I have the feeling that having greedy evaluation
>leads to "counterintuitive" tests
// Verify correct sequence of operations
order.verify(service, atleast(1)).saveEntity();
order.verify(service).commit();
// Ensure not too many calls to saveEntity
verify(service, times(1)).saveEntity();
A bit of explanation: I have an application to create / edit
glossaries, and I have the following concepts in my model:
* Template: Entry point of the knowledge level of the model (Fowler,
analysis patterns), defines the structure of the glossary, with an
attached style sheet defining its look. All classes in the knowledge
level model block types a glossary can be made of (for example
ContentType)
* Entry: Root of the operational level, an entry in the glossary, made
of blocks (for example Content)
* SAXExporter: A visitor that visits a glossary with its corresponding
template and style sheet, and outputs an XML stream through a SAX
ContentHandler
* EntryToXHTMLExporter: SAXExporter that visits an entry of a glossary
and outputs XHTML code with the content of the entry
I was trying to test this last class, and my approach was to mock the
ContentHandler object, and verify it was being called as expected, so
the generated XHTML was correct.
Thanks a lot, best regards
José
On 15 mar, 23:33, szczepiq <szcze...@gmail.com> wrote:
> >see if the non-greedy approach works better for this specific problem.
>
> Yeah, sure and find bugs later in the game.
>
> >it seems like you're mixing two different types of verifications.
>
> I think you raised a very good point but bear in mind that:
> - from the pragmatic standpoint, in this particular case, I think it is ok
> mix it... The test "shouldSaveEntityBeforeSessionIsComitted" looks good to
> me, it is still single behavior to me.
> - a developer would not bother separating the test. He reads the
> verification part and feels everything is covered. Then the bug hits him.
> Jose is a good example: He is *mixing* complex argument verification with
> verification in order and probably some other stuff. How come you don't
> suggest him to refactor his tests?
>
> @Jose, put the code to the pastebin.com and we will show you what is the
> best approach. I bet there isn't even a case for mocks :)
> @Kristofer, I think you understand why Mockito inOrder is greedy. I respect
> your different POV and good luck with non-greediness.
>
> Cheers,
> Szczepan Faber
>
> On Mon, Mar 15, 2010 at 9:26 PM, Kristofer Karlsson <
>
> kristofer.karls...@gmail.com> wrote:
>
> > On Mon, Mar 15, 2010 at 6:32 PM, szczepiq <szcze...@gmail.com> wrote:
>
> >> Hey
>
> >> >I'm completely
> >> >open to suggestions on how to improve my testing abilities :o)
>
> >> I don't know :) what you think?http://pastebin.com/Not knowing your
> >> 2010/3/15 José González Gómez <jose.gonzalez.go...@gmail.com>
> ...
>
> leer más »
mock.foo();
mock.foo();
mock.bar();
mock.foo();
I find this intuitive to pass (foo called exactly two times, then bar
called exactly one time, I don't mind what happens later):
inOrder.verify(mock, times(2)).foo();
inOrder.verify(mock, times(1)).bar();
I find this intuitive to fail (I read foo should be called
*exactly*one time, then bar exactly one time, I don't mind what
happens later):
inOrder.verify(mock, times(1)).foo();
inOrder.verify(mock, times(1)).bar();
I find this counterintuitive to fail (I read this "call foo at least
one time, then call bar, I don't mind about aditional calls to foo
between the foo call and the bar call"):
inOrder.verify(mock, atLeast(1)).foo();
inOrder.verify(mock, times(1)).bar();
I wanted to leave greediness aside because maybe this is just a
nomenclature problem, or a question of forbidding certain multiplicity
matchers in in order verifications... or maybe rethinking the whole in
order verification (I really haven't thought about the inners of call
matching, ignoring intermixed calls, etc.). I'm just discovering
mocks, so I don't know if in order verification is a widespread
feature and how they use to do it...
Best regards
José
On 15 mar, 18:32, szczepiq <szcze...@gmail.com> wrote:
> Hey
>
> >I'm completely
> >open to suggestions on how to improve my testing abilities :o)
>
> I don't know :) what you think?http://pastebin.com/Not knowing your code I
> have a feeling that it might be better to test it without mocks (eg.
> generate xml and compare the content by string or with some tool like xml
> unit).
>
> >* times( n ) -> something happens *exactly* n times (taking this into
> >account, I think it doesn't make sense to talk about greediness
> >here... what do you think?)
>
> Why not? Your initial post was the case with times(1)
>
> >I have the feeling that having greedy evaluation
> >leads to "counterintuitive" tests
>
> I agree. Greedy algorithm seems counter intuitive. However it prevents from
> bugs and is consistent with how Mockito works. I did some final amendments
> to the wiki, hopefully it is clear now :)
>
> Cheers,
> Szczepan Faber
>
> 2010/3/15 José González Gómez <jose.gonzalez.go...@gmail.com>
> ...
>
> leer más »
Best regards
José
On 16 mar, 10:47, José González Gómez <jose.gonzalez.go...@gmail.com>
wrote:
> > I don't know :) what you think?http://pastebin.com/Notknowing your code I
> ...
>
> leer más »
Apart from dealing with greediness, I am thinking of a seems-to-be
easier way of solving this.
When people needs to capture arguments, I believe, in most case, they
need to capture that
specific invocation. That means, if I called mockList.add(...) 3
times, and I do a verify with arg capture
once, what do most people expects? I believe most people will expect
to capture the first invocation,
instead of the last one of consecutive invocations.
Arg captor and times(n) seems a bit conflicting in usage.
Anyway, without considering whether they are conflict or not... Arg
captor is in fact a special
arg matcher which always matches, causing the 'greediness' to take
effect coz all consecutive
invocation are treated as "matches".
If we can make ArgumentCaptor aware of how many times to report
matches, and for the 'normal'
use case I described above, we make it report matches only once, then
it can capture consecutive
invocation one by one.
counter in ArgumentCaptor can be reset after each verification to
reuse arg captor instance.
eg.
ArgumentCaptor<Foo> argCaptor= ArgumentCaptor.create(Foo.class,
MATCH_ONCE); // MATCH_ONCE = 1
verify(mockList).add(argCaptor.capture());
// somehow auto reset the captor
verify(mockList).add(argCaptor.capture());
we can still have ArgumentCaptor works the old way if 2nd param is not
passed in create();
Is this is feasible way?
I'm open to suggestions...
Best regards
José
On 16 mar, 10:22, José González Gómez <jose.gonzalez.go...@gmail.com>
wrote:
> > >> I don't know :) what you think?http://pastebin.com/Notknowing your
> ...
>
> leer más »
Am I missing anything?
Best regards
José
On 17 mar, 09:25, Kristofer Karlsson <kristofer.karls...@gmail.com>
wrote:
> ArgumentCaptor actually remembers all invocations, not just the last one.
> getValue is just a shortcut to get the last item in the list.
>
> Also, if you don't want ArgumentCaptor to match for all invocations, you can
> use the and-matcher and put the actual matcher first and the ArgumentCaptor
> as the second argument:http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Add...
> > mockito+u...@googlegroups.com<mockito%2Bunsu...@googlegroups.com>
To unsubscribe from this group, send email to mockito+u...@googlegroups.com.
In fact I have overlooked the getAllValues() method before. :P
Please forgive my silliness