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

Calling Mockito.when multiple times on same object?

当尝试将 Mockito 与 Spring 一起使用时,通过 bean 声明创建 Mock 对象...

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

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

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

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

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

的预期结果

我只是想了解 Mockito 和这里的最佳实践,因为仅仅因为它似乎有效而使用某些东西似乎不是一个好主意...

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

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

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

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

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

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

有关必须使用"do"系列的情况的更多信息,请参阅我在Forming Mockito "grammars"

上的回答

简单的答案是:
当您编写 Mockito.when(object.fooMethod()).then() 时,实际上会调用 fooMethod()
另一点是我们不能第一次观察到它,因为它调用了模拟对象。但是当我们第二次写 when 时,我们对 fooMethod() 有一些行为(我们之前设置了它,在你的情况下它是异常)。

为了更好地检查这一点,您可以 spy 对象:

Bar spyBar = Mockito.spy(Bar.class)
when(spyBar.fooMethod()).then()...

fooMethod()会被实际调用。