Java 莫基托“;核实;检查当前状态,但不重置模拟调用

Java 莫基托“;核实;检查当前状态,但不重置模拟调用,java,junit,mockito,Java,Junit,Mockito,我在JUnit测试中使用Mockito。该测试是一个集成测试,测试整个场景,因此其中有许多断言和verify(mock)s 我的问题是,我正在编写一些生产代码,执行一些断言,然后验证mock是否有效,直到有一个相同的mock调用。请参见简化代码: interface I { void m1(); void m2(); } // some decorator class T implements I { public T(I decorated) { /*

我在JUnit测试中使用Mockito。该测试是一个集成测试,测试整个场景,因此其中有许多断言和
verify(mock)
s

我的问题是,我正在编写一些生产代码,执行一些断言,然后验证mock是否有效,直到有一个相同的mock调用。请参见简化代码:

interface I {
    void m1();
    void m2();
}

// some decorator
class T implements I {
    public T(I decorated) {
        /*...*/
    }

    /* ... */
}

public void testInvalid() {
    I m = mock(I.class);
    T t = new T(m);

    t.m1();
    assertEquals("m1", t.getLastMethod());
    verify(m).m1();

    t.m2();
    assertEquals("m2", t.getLastMethod());
    verify(m).m2();

    t.m1();
    assertEquals("m1", t.getLastMethod());
    verify(m).m1();

    // TooManyActualInvocations t.m1(): Wanted 1 time, but was 2 times ...
}

public void testValid() {
    I m = mock(I.class);
    T t = new T(m);

    t.m1();
    assertEquals("m1", t.getLastMethod());

    t.m2();
    assertEquals("m2", t.getLastMethod());

    t.m1();
    assertEquals("m1", t.getLastMethod());

    verify(m, times(2)).m1();
    verify(m).m2();
}
一个想法是在最后验证mock,但是假设有一个小的愚蠢的实现错误,它导致调用方法m1两次,m2一次,但不像我在
testInvalid
中预期的那样,但最终测试会通过。我希望我的考试早点失败。我如何做到这一点

多谢各位

感谢@Woozy Coder:

我没有提到,
reset
也将是一个选项,但是由于必须在verify和下一个相等存根调用之间调用它,因此我认为很难编写“好的”和正确的测试。应该有两种不同的模拟样式:

  • “后置条件”模仿莫基托的动作
  • “早期”模拟,即验证块后的隐式重置
  • 比如:

    earlyReset(m).after(
        new Runnable() {
            t.someMethodInvokingTwoStubs();
    
            verify(m).someMethod1();
            verify(m).someMethod2();
        }
    );
    

    Mockito是为了避免脆弱性而编写的,因此验证可以使最不特定的断言成为可能,从而允许实现在不改变测试的情况下发展。如果多次调用这些方法对您的测试系统没有关系,那么您不应该让Mockito检查它

    备选方案:

    • 使用
      至少
      至少一次
      确保调用完全发生,而不必担心调用该方法的额外次数
    • 如果该调用被存根化为具有返回值,则可以根据您所存根的数据的状态断言推断系统工作
    • 如果您确实需要在单个测试方法中更改存根或验证行为,那么您的mock可能会超出Mockito擅长的范围。使用单个方法的答案,或者编写一份手册,正确模拟相互关联的方法

    我遇到了类似的问题,并决定使用
    clearInvocations()
    (在Mockito 2.1中提供)


    使用
    reset()
    有一个缺点,即您也会松开存根,
    clearInvocations()
    只会清除调用。

    尝试一下,不要在一个测试方法中测试多个方法。一个测试应该有一个它测试的东西,最好是一个可能失败的东西。测试多种方法会使测试更加复杂。将测试分为多种测试方法将解决这个问题。@WoozyCoder谢谢,编辑了我的post@FlorianSchaetz这是一个集成测试。它测试一个有限状态机,分别是TDD,因为我想写一些生产代码来帮助我设计它。它使用的所有组件都经过单元测试,但是状态机使用了很多反射,我的测试是一个带有内部测试类和注释的场景。它从IDLState开始,IDLState应该调用
    onIdle
    ,然后我们选择一张票据,支付它,它应该返回IDLState并再次调用
    onIdle
    。这是一个安静而复杂的测试,但它是状态机实现以及功能文档的基础。一开始我希望能打电话给onIdle,最后在买票之后<代码>至少(2)结尾对我来说太模糊了。我认为Mockito不适合这样的集成测试,但是我非常喜欢Mockito,因为它很容易使用。这救了我一天!