【问题标题】:Mockito: Verify Mock (with "RETURNS_DEEP_STUBS") Returns More Calls Than ExpectedMockito:验证 Mock(使用“RETURNS_DEEP_STUBS”)返回比预期更多的调用
【发布时间】:2013-11-26 15:14:40
【问题描述】:

查看下面的代码,我只希望对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

经验教训

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

【问题讨论】:

    标签: java mockito


    【解决方案1】:

    由于 deeps 存根为 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);
    ...
    

    【讨论】:

    • 这很奇怪,知道为什么要计算设置吗?
    • 我认为这是因为许多其他人已经说过,验证 API 不支持深度存根,因此它与实际调用和存根调用没有区别:docs.mockito.googlecode.com/hg/org/mockito/…
    【解决方案2】:

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

    所以,我认为,为了让您的验证工作,您必须将您的 Mocks 重写为如下内容:

    @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()的调用。 - 这取决于你想在这里测试什么。

    【讨论】:

    • 这就是深度存根在内部工作的方式。 (docs.mockito.googlecode.com/hg/latest/org/mockito/…)。根据 crunchdog 的回答,我认为现在的问题是:为什么将 when 语句计为调用?
    • 啊,是的,但是在您的情况下,您在第一个模拟中多次主动调用 getSand() 。所以我猜是这样。并且文档多次声明验证仅适用于链中的最后一个模拟。
    • verify(mockSandBox, times(1)).getSand(); 模拟沙箱链中的最后一个模拟。我不确定你在暗示什么。
    • 我认为文档对链中最后一个模拟的含义是您在设置过程中调用的最后一件事。例如。 doA() 调用:when(mockSandBox.getSand().doA()).thenReturn(1);
    • 好的,我想我现在明白了。如果您将深度存根视为模拟对象树,那么您应该只验证叶子(“链中的最后一个模拟”),因为必须调用节点来设置叶子的行为。
    【解决方案3】:

    来自文档: " 验证 API 不支持“链接”,因此深度存根不会改变您进行验证的方式。”

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-20
    • 2020-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-20
    相关资源
    最近更新 更多