【问题标题】:Mockito verify that ONLY a expected method was calledMockito 验证仅调用了预期的方法
【发布时间】:2017-07-13 20:27:18
【问题描述】:

我正在一个项目中使用 Service 类和某种 Client 作为外观(不知道它是否是 设计模式世界中的正确术语,但我会尽量让自己清楚)。 Service 的方法可能非常昂贵,因为它们可能与一个或多个数据库进行通信、长时间检查等等,因此每个 Client 方法应该调用一个且只有一个 Service 方法。

Service 类结构类似于

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 或其他一些模拟库中是否有类似的东西?我不想对每种方法都使用verify(serviceMock, never()).serviceXXX(),因为正如我所说,Service 类将来可能会增长,我必须在每个测试中添加验证(对我来说这不是一个快乐的任务)所以我需要更一般的东西。

提前感谢您的回答。

编辑 #1

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

此外,verifyNoMoreInteractions 可能是一个不错的选择,即使每次测试都不鼓励使用它,也不需要额外的样板代码。

总而言之,possible duplicate 并没有解决我的问题。

还有一个问题:我正在为另一个团队编写的代码编写测试,我自己没有遵循 TDD 过程,所以我的测试应该是额外的防御,如this article 中所述verifyNoMoreInteractions 的模拟文档。我正在测试的方法通常很长,所以我需要检查被测方法是否只调用必要的服务而不调用其他服务(因为它们很昂贵,正如我所说)。也许verifyNoMoreInteractions 现在已经足够好了,但我希望看到同一个 API 创建者团队不会因为每次测试而气馁!

希望这有助于澄清我的观点和问题。最好的问候。

【问题讨论】:

标签: java mockito


【解决方案1】:
verify(serviceMock, times(1)).serviceA();
verifyNoMoreInteractions(serviceMock);

来自 verifyNoMoreInteractions 上的 Mockito 的 javadoc:

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

还有:

一句警告:一些进行了很多经典的、expect-run-verify 模拟的用户倾向于经常使用 verifyNoMoreInteractions(),即使在每个测试方法中也是如此。不建议在每个测试方法中使用 verifyNoMoreInteractions()。 verifyNoMoreInteractions() 是来自交互测试工具包的一个方便的断言。仅在相关时使用它。滥用它会导致过度指定、难以维护的测试。

【讨论】:

  • 这个+BDDMockito风格then(mock).should(times(1)).serviceA();
  • 如果您只检查一个调用times(1),则不需要。
【解决方案2】:

唯一方法是您可以可靠地验证您的服务只被调用一次,并且只从您指定的方法调用一次,而不是从任何 其他方法调用,是测试每个方法 并断言您的 serviceA 方法永远不会被调用。但是您正在测试所有其他方法无论如何,所以这不应该是那么大的提升......

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

虽然从代码编写的角度来看这是不可取的,但它打开了将您的服务分成更小、更负责任的块的大门,这样您就可以保证只调用一个特定的服务。从那里开始,围绕代码的测试用例和保证会变得更小、更可靠。

【讨论】:

  • @KilleKat:考虑到代码的编写方式现在,这是唯一可验证的方式。如果他们重构代码并划分职责,那么不,会有更好的方法。
  • verifyNoMoreInteractions() 怎么样?
  • @MatiasElorriaga:如果目的是验证模拟上的方法是否从未被调用,那么never() 将是合适的。这是关于意图; verifyNoMoreInteractions 暗示与整个模拟的交互发生了,但没有清楚地传达该模拟上的特定方法或字段没有与之交互。 never() 作为维护者和未来的测试者明确地告诉我,不,这个方法实际上从未被调用过,也不打算永远从此方法中调用。
【解决方案3】:

我认为您正在寻找的是 Mockito.verify 和 Mockito.times

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

verify(mockObject, atLeast(2)).someMethod("was called at least twice");
verify(mockObject, times(3)).someMethod("was called exactly three times");

这里有同样问题的另一个帖子:

Mockito: How to verify a method was called only once with exact parameters ignoring calls to other methods?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多