Mockito:验证模拟(使用“RETURNS_DEEP_STUBS”)返回比预期更多的调用

时间:2013-11-13 20:25:46

标签: java mockito

查看下面的代码,我只希望对getSand()的调用发生一次,但是测试失败了四次调用。这些电话在哪里发生?我想编写一个测试,以确保只对getSand()进行一次调用。

来源

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();
    }
}

输出

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)

详细信息 Java 1.6,JUnit 4.11,Mockito 1.9.5

经验教训

如果您将深层存根视为模拟对象树,那么您应该只验证叶子(“链中的最后一个模拟”),因为节点包含在设置叶子行为所需的调用链中。换句话说,在设置叶子期间调用节点

3 个答案:

答案 0 :(得分:10)

它将您的设置计为调用,因为深层存根是not supported in the verification API,并在第二次调用时抱怨:

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

我会跳过使用RETURNS_DEEP_STUBS并只使用另一个模拟:

...
@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);
...

答案 1 :(得分:9)

来自Answers.RETURNS_DEEP_STUBS的文档:

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

来自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();

所以,我认为,为了让你的验证工作,你必须将你的模拟改写成这样的东西:

@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();
}

或者只验证doA,doB和doC的调用,而不验证getSand()的调用。 - 这取决于你想在这里测试什么。

答案 2 :(得分:0)

来自文件: “Verification API不支持'链接',因此深度存根不会改变验证方式。”

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