Mockito验证仅调用了预期的方法

25
我正在参与一个项目,其中涉及一个“服务”类和一种充当门面的“客户端”(不知道是否是设计模式世界中的正确术语,但我会尽力让自己清晰明了)。由于“服务”类的方法可能需要与一个或多个数据库进行通信、进行长时间的检查等等,因此每个“客户端”方法都应该调用一个且仅一个“服务”方法。
“服务”类的结构大致如下:
public class Service {
    public void serviceA(){...}
    public SomeObject serviceB(){...}
    // can grow in the future
}

而且Client应该是类似这样的东西

public class Client {
    private Service myService; // Injected somehow
    public void callServiceA() {
        // some preparation
        myService.serviceA();
        // something else
    }

    public boolean callServiceB(){...}
}

Client 的测试类中,我希望有类似以下的内容:

public class ClientTest{
    private Client client; // Injected or instantiated in @Before method
    private Service serviceMock = mock(Service.class);

    @Test
    public void callServiceA_onlyCallsServiceA() {
        client.callServiceA();
        ????
    }
}

????部分,我想要类似verifyOnly(serviceMock).serviceA()这样的东西,它表示:"验证只调用了serviceMock.serviceA()一次,并且没有调用过Service类的其他方法"。在Mockito或其他Mocking库中是否有类似的功能?我不想为每个方法都使用verify(serviceMock, never()).serviceXXX(),因为正如我所说,Service类可能会在未来增长,我将不得不为每个测试添加验证(对我来说不是一件愉快的任务),所以我需要更通用的东西。
谢谢你提前回答。

EDIT #1

这篇文章和possible duplicate之间的区别在于,答案添加了不需要的模板代码,而这在我的情况下不是期望的,因为这是一个非常大的项目,我必须尽可能少地添加代码。

此外,即使在每个测试中都不鼓励使用,verifyNoMoreInteractions 也可以是一个很好的选择,无需额外的样板代码。
总之,可能的重复没有解决我的问题。
有另一个问题:我正在为另一个团队编写的代码编写测试,而我自己并没有遵循TDD流程,因此我的测试应该更加谨慎,正如this article所述,该文章在mockito文档中引用了verifyNoMoreInteractions。我正在测试的方法通常非常长,因此我需要检查被测试的方法是否仅调用必要的服务而不是其他服务(因为它们很昂贵,如我所说)。也许verifyNoMoreInteractions现在已经足够好了,但我想看到一些不被同一API创建者团队每次测试都不鼓励的东西!希望这有助于澄清我的观点和问题。最好的问候。

可能是 https://dev59.com/o2ct5IYBdhLWcg3wjd-H 的重复问题。 - Gonzalo Matheu
1
@GonzaloMatheu:这是一个很好的重复问题,因为这个问题类似,但问题比那更深入一些。 - Makoto
可能是Mockito verify no more interactions with any mock的重复问题。 - Tom
4个回答

30
verify(serviceMock, times(1)).serviceA();
verifyNoMoreInteractions(serviceMock);

根据 Mockito javadoc 上 verifyNoMoreInteractions 的说明:

您可以在验证模拟对象后使用此方法,以确保没有其他内容在您的模拟对象上调用。

另外:

警告:一些经典的 expect-run-verify 模拟使用者倾向于经常使用 verifyNoMoreInteractions(),甚至在每个测试方法中都使用它。不建议在每个测试方法中使用 verifyNoMoreInteractions()。verifyNoMoreInteractions() 是交互测试工具包中的一个方便的断言。只有在相关时才使用它。滥用它会导致过度规范化、难以维护的测试。


1
这是BDDMockito风格的代码:then(mock).should(times(1)).serviceA(); - Mateusz Chrzaszcz
2
如果您只检查一次调用,则不需要使用“times(1)”。 - KraffMann

0

你唯一可以可靠地验证你的服务只从你指定的方法中被调用一次且仅一次,而不是从任何其他方法中被调用,就是测试每个方法,并断言你的serviceA方法永远没有被调用。但你无论如何都在测试每个其他方法,所以这应该不算太难...

// In other test cases...
verify(serviceMock, never()).serviceA();

尽管从代码编写的角度来看这是不可取的,但它打开了将服务分解为更小、更可靠的部分的大门,以确保只调用一个特定的服务。从那里开始,你的测试用例和对代码的保证变得更小、更牢固。

@KilleKat:鉴于代码的编写方式,这是唯一可验证的方法。如果他们重构了代码并分担了责任,那么就不会有更好的方法了。 - Makoto
verifyNoMoreInteractions() 是怎么样的呢? - Matias Elorriaga
如果意图是验证模拟对象上的方法从未被调用,则使用never()是合适的。这关乎意图; verifyNoMoreInteractions暗示与模拟对象的交互已经发生,但并没有清楚地传达该模拟对象上的特定方法或字段未被交互。never()清晰地告诉我作为维护者和未来的测试人员,不,这个方法实际上从未被调用,并且不打算从这个方法中永远被调用。 - Makoto

0

0

为了补充@matias-elorriaga所写的内容,Mockito 4.0.0添加了方法only():

verify(serviceMock, only()).serviceA();

是...的简写

verify(serviceMock).serviceA();
verifyNoMoreInteractions(serviceMock);

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