在同一个对象上多次调用 Mockito.when?

2022-01-14 00:00:00 unit-testing spring mockito java

当尝试在 Spring 中使用 Mockito 时,通过 bean 声明创建 Mock 对象...

When trying to use Mockito with Spring, by creating the Mock object via a bean declaration...

<bean id="accountMapper" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="org.example.persistence.mybatis.mappers.AccountMapper" /> 
</bean>     

...我在多次调用 Mockito.when 而不重置 Mock 对象时发现了一些奇怪的行为,例如:

...I found some strange behavior when calling Mockito.when multiple times without reseting the Mock object, for example:

Mockito.when(this.accountMapper.createBadGrammarException()).thenThrow(new BadSqlGrammarException("Bla", null, new SQLException()));

一旦在测试期间(在同一个模拟上)多次调用此代码(Mockito.when"),测试就会失败并出现错误(BadSqlGrammerException,即使这个异常是实际预期的 - 我会如果我不抛出异常就会失败,并且手动抛出它可以正常工作).这是预期的行为吗?Mockito 似乎建议每次都创建一个新的模拟,这意味着为每个方法创建 DAO...?

As soon as this code (the "Mockito.when") is called multiple time during the test (on the same mock), the tests fails with an error (BadSqlGrammerException even if this exception was what was actually expected - I do get a failure if I don't throw the exception, and throwing it manually works fine). Is this expected behavior? Mockito seems to suggest creating a new mock every time, which would mean creating the DAO for each method...?

当我调用 Mockito.when 方法两次时究竟会发生什么?模拟应该如何反应?替换行为?忽略它?不幸的是,大多数搜索只产生关于如何为多次调用方法本身返回不同结果的结果,而不是多次调用 Mockito.when...

What exactly happens when I call the Mockito.when method two times? How should the mock react? Replace the behavior? Ignore it? Unfortunately most searches only yield results for how to return different results for multiple calls to the method itself, but not what is to be expected for multiple calls to Mockito.when...

我只是想了解 Mockito 和这里的最佳实践,因为仅仅因为它看起来可以工作就去做似乎是个坏主意......

I'm simply trying to understand Mockito and best practices here, because going with something just because it SEEMS to works seems to be a bad idea...

推荐答案

Mockito.when 的一个问题是你传递给它的参数是你试图存根的表达式.因此,当您对同一方法调用两次使用 Mockito.when 时,第二次使用它时,您实际上会得到第一次存根的行为.

One of the problems with Mockito.when is that the argument you pass to it is the expression that you're trying to stub. So when you use Mockito.when twice for the same method call, the second time you use it, you'll actually get the behaviour that you stubbed the first time.

我实际上建议不要使用 Mockito.when.当你使用它时,你可能会陷入许多陷阱——在很多情况下,你需要一些其他的语法.更安全"的替代语法是 Mockito 方法的do"系列.

I actually recommend NOT using Mockito.when. There are many traps that you can fall into when you use it - quite a few cases when you need some other syntax instead. The "safer" alternative syntax is the "do" family of Mockito methods.

doReturn(value).when(mock).method(arguments ...);
doThrow(exception).when(mock).method(arguments ...);
doAnswer(answer).when(mock).method(arguments ...);

所以在你的情况下,你想要

So in your case, you want

doThrow(new BadSqlGrammarException(??, ??, ??)).when(accountMapper).createBadGrammarException();

如果您刚开始使用 Mockito,那么我建议您学习使用do"系列.它们是模拟 void 方法的唯一方法,Mockito 文档特别提到了这一点.但是只要可以使用 Mockito.when 就可以使用它们.因此,如果您使用do"系列,您最终会在测试中获得更高的一致性,并减少学习曲线.

If you are starting out with Mockito, then I recommend that you learn to use the "do" family. They're the only way to mock void methods, and the Mockito documentation specifically mentions that. But they can be used whenever Mockito.when can be used. So if you use the "do" family, you'll end up with more consistency in your tests, and less of a learning curve.

有关必须使用do"族的情况的更多信息,请参阅我在 Forming Mockito 上的回答语法"

For more information about the cases when you must use the "do" family, see my answer on Forming Mockito "grammars"

相关文章