Mockito参数匹配器(例如any
、argthat
、eq
、same
和argumentcaptor.capture()
)的行为与Hamcrest匹配器非常不同。
>
Mockito匹配器经常导致InvalidUseOfMatchersException,即使在使用任何匹配器之后很久才执行的代码中也是如此。
Mockito匹配器受制于奇怪的规则,例如,只有当给定方法中的一个参数使用匹配器时,才要求对所有参数使用Mockito匹配器。
Mockito匹配器在重写answer
s或使用(Integer)any()
等时会导致NullPointerException。
用Mockito匹配器以某些方式重构代码可能会产生异常和意外行为,并可能完全失败。
为什么Mockito匹配器是这样设计的,它们是如何实现的?
Mockito匹配器是静态方法和对这些方法的调用,它们在调用when
和verify
期间充当参数。
Hamcrest匹配器(存档版本)(或Hamcrest样式的匹配器)是无状态的通用对象实例,它们实现matcher
,并公开一个方法matches(T)
,如果对象匹配匹配器的条件,则返回true。它们的目的是没有副作用,通常用于如下断言中。
/* Mockito */ verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));
Mockito匹配器与Hamcrest样式的匹配器是分开的,因此匹配表达式的描述直接适合于方法调用:Mockito匹配器返回t
,其中Hamcrest匹配器方法返回匹配器对象(类型为matcher
)。
Mockito匹配器通过静态方法调用,例如eq
、any
、gt
和org.Mockito.matchers
和org.Mockito.additionalmatchers
上的startswith
。还有适配器,它们在Mockito版本中发生了变化:
matchers
具有一些调用(例如intthat
或argthat
)是直接接受Hamcrest matchers作为参数的Mockito matchers。argumentmatcher
扩展了org.Hamcrest.matcher
,它用于内部Hamcrest表示,是Hamcrest matcher基类,而不是任何类型的Mockito matcher。matchers
调用短语为intthat
或argthat
wrapargumentmatcher
的对象,这些对象不再实现org.hamcrest.matcher
但使用方式类似。诸如argthate
和intthat
之类的Hamcrest适配器仍然可用,但已转移到mockitoHamcrest
。无论匹配器是Hamcrest还是简单的Hamcrest风格,它们都可以如下所示进行调整:
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));
在上面的语句中:foo.setPowerLevel
是一个接受int
的方法。is(greaterThan(9000))
返回匹配器
,该匹配器不能用作setpowerlevel
参数。Mockito匹配器intthe
包装Hamcrest-style匹配器并返回int
,以便它可以作为参数出现;像gt(9000)
这样的Mockito匹配器会将整个表达式包装成单个调用,如示例代码的第一行所示。
when(foo.quux(3, 5)).thenReturn(true);
当不使用参数匹配器时,Mockito会记录参数值,并将其与equals
方法进行比较。
when(foo.quux(eq(3), eq(5))).thenReturn(true); // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different
像eq
和gt
这样的匹配器接受参数值;理想情况下,这些值应该在stubbing/验证开始之前计算。在嘲笑另一个调用的中间调用一个嘲笑可能会干扰存根。
匹配器方法不能用作返回值;例如,在Mockito中,无法将thenreturn(anyInt())
或thenreturn(any(foo.class))
短语化。Mockito需要确切地知道在stubbing调用中返回哪个实例,并且不会为您选择任意的返回值。
匹配器存储在一个称为ArgumentMatcherStorage的类中的堆栈中(作为HamCrest样式的对象匹配器)。MockitoCore和Matchers各自拥有一个ThreadSafeMockingProgress实例,该实例静态地包含一个ThreadLocal保存MockingProgress实例。正是这个MockingProgressImpl持有一个具体的论证MatcherStorageImpl。因此,mock和matcher状态是静态的,但在Mockito和Matchers类之间线程范围一致。
大多数匹配器调用只添加到这个堆栈中,但和
、或
以及不
等匹配器例外。这完全符合(并依赖于)Java的计算顺序,该顺序在调用方法之前从左到右计算参数:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
这将:
呼叫命令不仅重要,而且是让这一切工作的原因。将匹配器提取到变量一般不起作用,因为它通常会改变调用顺序。然而,将匹配器提取到方法非常有效。
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.
堆栈经常变化,以至于Mockito不能非常仔细地监控它。它只能在与Mockito或mock交互时检查堆栈,并且必须接受匹配器,而不知道它们是立即使用还是意外放弃。理论上,当或验证
时,堆栈在调用之外应该总是空的,但是Mockito不能自动检查这一点。您可以使用
mockito.validateMockitousage()
手动检查。
在对
when
的调用中,Mockito实际上调用了有问题的方法,如果您为抛出异常(或者需要非零或非NULL值)而对该方法进行了截断,则该方法将抛出异常。doReturn
和doAnswer
(etc)不调用实际的方法,通常是一种有用的替代方法。
如果您在stubbing过程中调用了一个mock方法(例如,为
eq
匹配器计算答案),Mockito将根据该调用检查堆栈长度,并且很可能失败。
如果您试图做一些不好的事情,比如stubbing/verification final方法,Mockito将调用real方法,并在堆栈中留下额外的匹配器。
final
方法调用可能不会引发异常,但在下次与模拟交互时,您可能会从杂散匹配器中获得InvalidUseOfMatchersException。
>
invaliduseofmatchersexception:
>
检查是否每个参数都有一个匹配器调用(如果您使用匹配器的话),并且是否在
When
或Verify
调用之外使用过匹配器。匹配器永远不应该用作stubbed返回值或字段/变量。
带有原语参数的NullPointerException:
(Integer)any()
返回null,而any(Integer.class)
返回0;如果您期望的是int
而不是整数,这可能会导致NullPointerException
。在任何情况下,首选anyint()
,它将返回零并跳过自动装箱步骤。
NullPointerException或其他异常:调用
when(foo.bar(any())).thenreturn(baz)
实际上将调用foo.bar(null)
,在接收到空参数时,您可能已经将其剪断以引发异常。切换到doreturn(baz).when(foo).bar(any())
跳过stubbed行为。
>
使用MockitoJUnitRunner,或者在
teardown
或@after
方法中显式调用validateMockitousage
(运行程序将自动为您执行此操作)。这将有助于确定您是否滥用了匹配器。
出于调试目的,请直接在代码中添加对
ValidateMockitousage
的调用。如果堆栈上有任何东西,这将抛出,这是不良症状的良好警告。
问题内容: 争论的Mockito匹配器(如,,,,和)从Hamcrest匹配器表现非常不同。 匹配器经常导致,即使在使用任何匹配器很长时间后执行的代码中也是如此。 匹配器遵循怪异的规则,例如,如果给定方法中的一个参数使用匹配器,则仅要求对所有参数使用Mockito匹配器。 当覆盖或使用时,匹配器可能导致 。 使用Mockito匹配器以某些方式重构代码会产生异常和意外行为,并且可能会完全失败。 为什
鉴于以下Mockito语句: 假设mock.method()语句将返回值传递给when(),Mockito如何为mock创建代理?我想这使用了一些CGLib的东西,但我想知道这是如何在技术上完成的。
我是Elasticsearch新手,对匹配查询的工作方式感到困惑。我有以下映射: 我批量导入了以下文档 我验证了所有文档都已成功加载。然后我执行匹配查询: 它只返回#1文档。我在这里感到困惑。为什么它不返回所有三个文档?我应该使用什么查询来返回这三个文档,因为它们在字段中都有“quiet”根单词? 谢谢和问候。
问题内容: 我有这样声明的方法 这个枚举 问题:如何模拟通话?我无法比拟。 以下无效: 问题答案: 将达到目的: 附带说明:考虑使用静态导入: 模拟变得更短:
问题内容: 使用Mockito,我想在其参数列表中使用方法调用,但是我没有找到如何编写该方法的方法。 我只想要像Mockito那样的东西? 问题答案: 我会尝试
使用Mockito,我想一个参数列表中包含的方法调用,但我没有找到如何编写这一点。 我只想要类似于的东西,如何使用Mockito实现这一点?