使用 Mockito 的 ArgumentCaptor 类来匹配子类
下面的代码显示了我的问题。实际上,我尝试使用 Mockito 的 ArgumentCaptor 来验证某个具体类是否调用过一次方法。如果可能的话,我想在这里使用 ArgumentCaptor,但我开始怀疑我需要使用自定义 ArgumentMatcher。
问题是行 Mockito.verify(mocked).receive(captor.capture());
(编辑:将其添加到下面的代码中)失败并出现 TooManyActualInitations 异常(2 而不是 1) 。我想了解为什么会发生这种情况 - 是 Mockito 的实现不佳还是由泛型的类型擦除引起的限制?
public class FooReceiver {
public void receive(Foo foo) {
}
}
public interface Foo {
}
public class A implements Foo {
}
public class B implements Foo {
}
public class TestedClass {
private FooReceiver receiver;
public TestedClass(FooReceiver receiver) {
this.receiver = receiver;
}
public void doStuff() {
receiver.receive(new A());
receiver.receive(new B());
}
}
public class MyTest {
@Test
public void testingStuff() {
// Setup
FooReceiver mocked = Mockito.mock(FooReceiver.class);
TestedClass t = new TestedClass(mocked);
// Method under test
t.doStuff();
// Verify
ArgumentCaptor<B> captor = ArgumentCaptor.forClass(B.class);
Mockito.verify(mocked).receive(captor.capture()); // Fails here
Assert.assertTrue("What happened?", captor.getValue() instanceof B);
}
}
编辑: 对于任何感兴趣的人,我最终这样做了:
// Verify
final B[] b = new B[1];
ArgumentMatcher<B> filter = new ArgumentMatcher<B>() {
@Override
public boolean matches(Object argument) {
if(argument instanceof B) {
b[0] = (B) argument;
return true;
}
return false;
}
}
Mockito.verify(mocked).receive(Mockito.argThat(filter));
The below code shows my problem. Effectively, I am trying to use Mockito's ArgumentCaptor to verify that a method was called once with a certain concrete class. I would like to use ArgumentCaptor here if possible, but I am beginning to suspect I need to use a custom ArgumentMatcher instead.
The problem is that the line Mockito.verify(mocked).receive(captor.capture());
(Edit: Added this to the code below) fails with a TooManyActualInvocations exception (2 instead of 1). I would like to understand why this is happening - is it poor implementation of Mockito or a limitation caused by type erasure of generics?
public class FooReceiver {
public void receive(Foo foo) {
}
}
public interface Foo {
}
public class A implements Foo {
}
public class B implements Foo {
}
public class TestedClass {
private FooReceiver receiver;
public TestedClass(FooReceiver receiver) {
this.receiver = receiver;
}
public void doStuff() {
receiver.receive(new A());
receiver.receive(new B());
}
}
public class MyTest {
@Test
public void testingStuff() {
// Setup
FooReceiver mocked = Mockito.mock(FooReceiver.class);
TestedClass t = new TestedClass(mocked);
// Method under test
t.doStuff();
// Verify
ArgumentCaptor<B> captor = ArgumentCaptor.forClass(B.class);
Mockito.verify(mocked).receive(captor.capture()); // Fails here
Assert.assertTrue("What happened?", captor.getValue() instanceof B);
}
}
EDIT:
For anyone interested, I ended up doing this:
// Verify
final B[] b = new B[1];
ArgumentMatcher<B> filter = new ArgumentMatcher<B>() {
@Override
public boolean matches(Object argument) {
if(argument instanceof B) {
b[0] = (B) argument;
return true;
}
return false;
}
}
Mockito.verify(mocked).receive(Mockito.argThat(filter));
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
据我所知,这是一个限制/实施不佳。
当查看 org.mockito.internal.matchers.CapturingMatcher 时,它
意味着它匹配每个参数/类。
这会导致
org.mockito.internal.matchers.CapturingMatcher#getAllValues
返回一个List
,但实际上包含一个A
和一个B
在运行时尝试将它们获取为B
时会导致ClassCastException
。这应该可以通过改变 org.mockito.ArgumentCaptor 来解决,方法是传递它的 Class clazz 到
CapturingMatcher
中,从而正确传递类型信息,从而实现正确的matches
实现并消除对强制转换/原始类型使用的需要。As far as I can tell this is a limitation / poor implementation.
When looking at
org.mockito.internal.matchers.CapturingMatcher
there ismeaning it matches every argument / class.
This results in
org.mockito.internal.matchers.CapturingMatcher#getAllValues
returning aList<B>
but actually containing oneA
and oneB
resulting in aClassCastException
during runtime when trying to get them asB
.This should be solvable by changing
org.mockito.ArgumentCaptor
in a way that it passes itsClass<? extends T> clazz
into theCapturingMatcher
and therefore passing the type information along properly, enabling a propermatches
implementation and removing the need for the cast / raw type usage.您还可以使用 Mockito.isA 验证参数是否属于特定类:
Mockito JavaDoc
You can also use Mockito.isA to verify that the argument is of a specific class:
Mockito JavaDoc
该方法将被调用两次,因此您需要执行以下操作:
The method will be called twice so you need to do this:
来源:luk2302的答案(在代码中实现)
java似乎不喜欢“instanceof T”。使用匿名类而不是私有静态类也给我带来了覆盖方面的麻烦。
这比提问者 (user545680) 聪明的 ArgumentMatcher 解决方案更短、更简洁。
通用版本:(有点长,不硬编码“B”,推荐)
使用 ArgumentMatcher/CapturesArguments 的通用版本:(只是为了展示它是如何工作的 - 它更长)
Source: luk2302's answer (implemented it in code)
java didn't seem to like "instanceof T". Using an anonymous class instead of a private-static-class also gave me troubles with the override.
This is a bit shorter and cleaner-looking than the asker's (user545680) clever ArgumentMatcher solution.
Generic version: (bit longer, does not hardcode "B", recommended)
Generic version using ArgumentMatcher/CapturesArguments: (just to show how it works - it is longer)