Mockito的“verify”检查当前状态,但不重置模拟调用。

4

我在我的 JUnit 测试中使用 Mockito。这是一项集成测试,测试整个场景,因此其中有很多断言和 verify(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();
}

一种想法是在最后验证模拟对象,但是假设有一个小的愚蠢实现错误导致方法m1被调用两次,m2被调用一次,但不是我在testInvalid中期望的方式,而是在结束时测试会通过。 我希望我的测试能够尽早失败。我该怎么做?
谢谢。
感谢@Woozy Coder :
没有提到reset也是一个选择,但由于必须在verify和下一个相等的存根调用之间调用它,这使得编写“好看”的正确测试很困难,我认为应该有两种不同的模拟样式:
1.像Mockito一样的"后置条件"模拟 2. "早期"模拟,这将是在verify块后的隐式重置
类似于:
earlyReset(m).after(
    new Runnable() {
        t.someMethodInvokingTwoStubs();

        verify(m).someMethod1();
        verify(m).someMethod2();
    }
);

1
尝试一下这个答案 - kazbeel
不要在一个测试方法中测试多个方法。一个测试应该只测试一件事情,最好只有一件可能失败的事情。测试多个方法会使你的测试更加复杂。将你的测试分成多个测试方法可以解决这个问题。 - Florian Schaetz
@WoozyCoder 谢谢,我编辑了我的帖子。 - Aitch
@FlorianSchaetz,这是一项集成测试。它测试了一个有限状态机,而且我以TDD的方式编写代码,因为我想编写一些生产代码来帮助设计它。它所使用的所有组件都经过了单元测试,但状态机使用了很多反射,并且我的测试是使用内部测试类和注释的场景。 - Aitch
2个回答

4

0

Mockito旨在避免脆弱性,以便验证可以进行最不具体的断言,从而允许实现在不更改测试的情况下演变。如果您的测试系统多次调用这些方法并不重要,则不应要求Mockito进行检查。

替代方案:

  • 使用atLeastatLeastOnce确保调用发生了,而不必担心方法被调用了多少额外次。
  • 如果调用被存根以返回值,则从关于您已存根数据的状态断言中推断出系统是否正常工作。
  • 如果您确实需要在单个测试方法中更改存根或验证行为,则您的模拟可能会超出Mockito处理范围。对于单个方法,请使用Answer,或编写手动Fake以正确模拟相互关联的方法。

我考虑使用状态机实现一个售票机。它从IdleState开始,应该调用onIdle,然后我们选择一张票,支付后它应该返回到IdleState并再次调用onIdle。这是一个相当复杂的测试,但它是状态机实现以及功能文档的基础。在开始时,我期望调用onIdle,并在支付票后最终调用。对我来说,最后的“atLeast(2)”太模糊了。我认为Mockito不适合这样的集成测试,但我非常喜欢Mockito,因为它很容易使用。 - Aitch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接