如何使用 ArgumentCaptor 进行存根?

Posted

技术标签:

【中文标题】如何使用 ArgumentCaptor 进行存根?【英文标题】:How to use ArgumentCaptor for stubbing? 【发布时间】:2012-08-31 00:37:32 【问题描述】:

在 Mockito documentation 和 javadocs 它说

建议使用 ArgumentCaptor 进行验证,但不要使用存根。

但我不明白 ArgumentCaptor 如何用于存根。有人可以解释上面的语句并展示如何使用 ArgumentCaptor 进行存根或提供一个链接来说明它是如何完成的吗?

【问题讨论】:

这里的解释很简短:dzone.com/articles/… 【参考方案1】:

线

when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);

会做同样的事情

when(someObject.doSomething(Matchers.any())).thenReturn(true);

所以,当存根没有附加值时使用 argumentCaptor.capture()。使用 Matchers.any() 可以更好地显示实际发生的情况,因此更好的可读性。 使用 argumentCaptor.capture(),您无法读取真正匹配的参数。 当您有更多信息(预期参数的类)时,您可以使用更具体的匹配器来改进您的测试,而不是使用 any()。

还有一个问题: 如果在存根时使用 argumentCaptor.capture(),则不清楚在验证后应该捕获多少个值。我们希望在验证期间捕获一个值,而不是在存根期间,因为此时还没有要捕获的值。那么参数捕获器在存根期间捕获方法捕获了什么?它捕获任何东西,因为还没有任何东西要捕获。 我认为这是未定义的行为,我不想使用未定义的行为。

【讨论】:

回答你的问题,argumentCaptor.capture() 确实捕获了传递给存根方法的值,这对于你有类似的东西很有用:“someObject.doSomething(new OtherObject(4)) " 在这种情况下,捕获者将获得那个 OtherObject 实例,然后您可以使用它来验证是否通过了 4 @raspacorp argumentCaptor.capture() 确实捕获了在验证方法中使用时传递给存根方法的值。在存根时(when 方法)使用时不会这样做,因为那时没有要捕获的内容(尚未调用要测试的逻辑)。【参考方案2】:

假设,如果搜索让你找到了这个问题,那么你可能想要这个:

doReturn(someReturn).when(someObject).doSomething(argThat(argument -> argument.getName().equals("Bob")));

为什么?因为像我一样,您很重视时间,而且您不会仅仅为了单一测试场景而实施.equals

而且 99% 的测试会因 Mock 返回 null 而崩溃,在合理的设计中,您将不惜一切代价避免返回 null,使用 Optional 或迁移到 Kotlin。这意味着 verify 不需要经常使用,而且 ArgumentCaptors 写起来太乏味了。

【讨论】:

【参考方案3】:

假设以下方法进行测试:

public boolean doSomething(SomeClass arg);

Mockito 文档说您应该以这种方式使用 captor:

when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);
assertThat(argumentCaptor.getValue(), equalTo(expected));

因为你可以在存根期间只使用匹配器:

when(someObject.doSomething(eq(expected))).thenReturn(true);

但验证是另一回事。如果您的测试需要确保使用特定参数调用此方法,请使用ArgumentCaptor,它就是为这种情况而设计的:

ArgumentCaptor<SomeClass> argumentCaptor = ArgumentCaptor.forClass(SomeClass.class);
verify(someObject).doSomething(argumentCaptor.capture());
assertThat(argumentCaptor.getValue(), equalTo(expected));

【讨论】:

感谢您的回答。我有个问题。在第三个代码块中,我们知道只有当 expected 被传递给 doSomething 时才会返回 true。但是第二个代码块什么时候返回 true 呢?或者在这种情况下 someObject 是否总是为 someMethod 返回 true? 嗯,我相信您的意思是“但是 third 代码块中何时返回 true?”。在第三个代码块中,我们只是不关心返回值并让它成为默认值。对于布尔值,它是 false,而不是 true 不,我将所有灰色背景块都算作代码块。包括第一个班轮。我指的是 when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true); 啊,对不起。是的,在这种情况下,总是会返回 true。 不确定“不与存根一起使用”的原因很简单。匹配器不会给我们实际的预期参数(只是类型),并导致测试通过,尽管参数可能是错误的。

以上是关于如何使用 ArgumentCaptor 进行存根?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 mockito 捕获特定类型的列表

如何对从包中导入的方法进行存根和间谍活动?

如何存根/注入视图控制器以在 iOS 5 运行时进行基于状态的测试?

如何在 SinonJS 中对模拟方法的返回值进行存根

Mongoose:我如何避免回调地狱,同时允许对不返回承诺的 mongoose 方法进行存根?

如何在 WebStorm 编辑器中生成代码存根