24

Looking at the code below, I only expect the call to getSand() to happen once, but the test is failing with four calls to it. Where are these calls happening? I want to write a test to insure that only one call is made to getSand().

Source

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class DeepSandTest {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    SandBox mockSandBox;

    @Test
    public void should(){
        when(mockSandBox.getSand().doA()).thenReturn(1);
        when(mockSandBox.getSand().doB()).thenReturn(1);
        when(mockSandBox.getSand().doC()).thenReturn(1);

        DeepSand deepSand = new DeepSand(mockSandBox);
        deepSand.getTipple();

        verify(mockSandBox, times(1)).getSand();
    }

    public class DeepSand{

        private SandBox sandBox;

        public DeepSand(SandBox sandBox) {
            this.sandBox = sandBox;
        }

        public void getTipple(){
            Sand sand = sandBox.getSand();
            sand.doA();
            sand.doB();
            sand.doC();
        }
    }

    public interface SandBox{
        public Sand getSand();
    }

    public interface Sand{
        public Integer doA();
        public Integer doB();
        public Integer doC();
    }
}

Output

org.mockito.exceptions.verification.TooManyActualInvocations: 
mockSandBox.getSand();
Wanted 1 time:
-> at DeepSandTest.should(DeepSandTest.java:26)
But was 4 times. Undesired invocation:
-> at DeepSandTest.should(DeepSandTest.java:20)

Details Java 1.6, JUnit 4.11, Mockito 1.9.5

Lessons Learned

If you think of deep stubs as a tree of mock objects, then you should only verify the leaves ("last mock in the chain") because the nodes are included in the call chain needed to setup the behavior of the leaves. To phrase this another way, the nodes are called during the setup of the leaves.

Community
  • 1
  • 1
Rylander
  • 19,449
  • 25
  • 93
  • 144

3 Answers3

17

It's counting your setup as invocations since deeps stubs is not supported in the verification API, and complains on the second call which is:

when(mockSandBox.getSand().doB()).thenReturn(1);

I would skip using RETURNS_DEEP_STUBS and just use another mock:

...
@Mock
SandBox mockSandBox;

@Mock
Sand sand;

@Test
public void should(){
    when(mockSandBox.getSand()).thenReturn(sand);
    when(sand.doA()).thenReturn(1);
    when(sand.doB()).thenReturn(1);
    when(sand.doC()).thenReturn(1);
...
Eel Lee
  • 3,513
  • 2
  • 31
  • 49
crunchdog
  • 13,078
  • 3
  • 23
  • 19
  • That's weird, any idea why it is counting the setup? – Rylander Nov 13 '13 at 20:45
  • I think it's because of what many others are saying already, the verification API don't support deep stubs, so it cannot differ from real invocation and stubbed invocation: http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#RETURNS_DEEP_STUBS – crunchdog Nov 14 '13 at 09:01
13

From the documentation of Answers.RETURNS_DEEP_STUBS:

Please see the {@link org.mockito.Mockito#RETURNS_DEEP_STUBS} documentation for more details.

From Mockito.RETURNS_DEEP_STUBS:

Verification only works with the last mock in the chain. You can use verification modes. 
[...]
when(person.getAddress(anyString()).getStreet().getName()).thenReturn("deep");
[...]
inOrder.verify(person.getAddress("the docks").getStreet(), times(1)).getName();

So, I think, in order to get your verifies to work, you have to rewrite your Mocks to something like this:

@Mock
SandBox mockSandBox;

@Mock
Sand mockSand;

@Test
public void should()
{
    when( mockSand.doA() ).thenReturn( 1 );
    when( mockSand.doB() ).thenReturn( 1 );
    when( mockSand.doC() ).thenReturn( 1 );

    when( mockSandBox.getSand() ).thenReturn( mockSand );

    DeepSand deepSand = new DeepSand( mockSandBox );
    deepSand.getTipple();

    verify( mockSandBox, times( 1 ) ).getSand();
}

Or only verify the invocations of doA, doB and doC and not verify the invocation of getSand(). - Which depends on what exactly you want to test for here.

hagbard
  • 695
  • 3
  • 13
  • That is how deep stubs work internally. (http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#RETURNS_DEEP_STUBS). Based on crunchdog's answer I believe the question is now: Why is it counting the when statements as invocations? – Rylander Nov 13 '13 at 20:53
  • Ah yes, but in your case your are actively calling getSand() on the first mock several times. So that i guess is it. And the doc states several times that verification only works on the last mock in the chain. – hagbard Nov 13 '13 at 21:00
  • in `verify(mockSandBox, times(1)).getSand();` the mockSandbox **is** the last mock in the chain. I am not sure what you are implying. – Rylander Nov 13 '13 at 22:01
  • I think what the documentation means with last mock in the chain, is the last thing you call during setup. E.g. the doA() calls : when(mockSandBox.getSand().doA()).thenReturn(1); – hagbard Nov 14 '13 at 15:15
  • Ok I think I get it now. If you think of deep stubs as a tree of mock objects, then you should only verify the leaves ("last mock in the chain") because the nodes have to be called to setup the behavior of the leaves. – Rylander Nov 18 '13 at 17:52
0

From documention: " Verification API does not support 'chaining' so deep stub doesn't change how you do verification."

Source: http://mockito.googlecode.com/svn/tags/1.8.3/javadoc/org/mockito/Mockito.html#RETURNS_DEEP_STUBS

Areo
  • 928
  • 5
  • 12
  • 2
    I am using Mockito 1.9.5 which does not have that warning anymore. (http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#RETURNS_DEEP_STUBS) – Rylander Nov 13 '13 at 20:39
  • `Verification only works with the last mock in the chain.` Link to latest doc : https://www.javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#RETURNS_DEEP_STUBS – Number945 Aug 15 '21 at 08:15