使用 Mockito 从链式方法中捕获参数

Capture Arguments from Chained Methods Using Mockito

我正在使用 Mockito 为现有 class 编写一些 Java 单元测试。但是,我无法提取此 class 的对象作为嵌套调用的一部分调用方法的参数。

示例:

public class SomeClass {

  public SomeClass() {
    //...
  }

  public someMethod(Foo foo) {
    // Bar bar = ...
    someOtherMethod(bar);
  }

  private someOtherMethod(Bar bar) {
    //...
  }
}

我想捕获 someOtherMethod() 的参数并验证 bar 是否正确。我可以使用 ArgumentCaptor 或 Mockito 中的其他功能来做到这一点吗?

一个选项是用 class 覆盖 SomeClass 来存储这些参数并使它们可供您进行验证。一个优雅的选项是 custom argument matchers.

模拟 someOtherMethod 不太可能为您提供有用的结果。 someMethod 是您 public API 的一部分,但 someOtherMethod 不是;通常,测试契约(someMethod 的 return 价值和副作用)很重要,但测试实现(someMethod 调用的私有方法)是一种反模式。

  • 理想情况下,只需测试 someMethod 的结果和副作用,并将其实现视为黑盒。您可能甚至不需要为此使用 Mockito。这是最纯粹的测试驱动开发形式:它使您可以自由地重构 someMethod,也许完全省略对 someOtherMethod 的调用或缓存结果(等)而无需编辑您的测试。
  • 如果 someOtherMethod 调用外部服务,请将其设置为您的 SomeClass 实例上的可访问或可设置字段,然后模拟该服务。 与其他服务的交互可以合理地视为一般合同的一部分;这确实是 Mockito 的设计目的。
  • 如果 someOtherMethod 足够独立以至于您想单独测试 someMethod,请考虑将其设为包私有或受保护。这样你就可以在你的测试中用一个虚拟的实现来覆盖它,也可能在你的代码的其他地方——毕竟,它现在是你的 someMethod 合同的一个测试部分,对吧?如果它太大或不透明,您还可以考虑将其设为单独的可模拟对象 class。

另请参阅 Whosebug 上的 "How do I unit test private methods?" in Programmers.SE, or "How to use Mockito when we cannot pass a mock object to an instance of a class"

是的。

@Spy
private SomeClass someClassSpy;

@Captor
private ArgumentCaptor<Bar> barCaptor;

...

verify(someClassSpy).someOtherMethod(barCaptor.capture());
Bar bar = barCaptor.getValue();
assertThat(bar, is(...));