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));
You can also use Mockito.isA to verify that the argument is of a specific class:
verify(mock).init(isA(ExpectedClass.class));
Mockito JavaDoc
As far as I can tell this is a limitation / poor implementation.
When looking at org.mockito.internal.matchers.CapturingMatcher
there is
public boolean matches(Object argument) {
return true;
}
meaning it matches every argument / class.
This results in org.mockito.internal.matchers.CapturingMatcher#getAllValues
returning a List<B>
but actually containing one A
and one B
resulting in a ClassCastException
during runtime when trying to get them as B
.
List<Object> arguments; // the invocations
// adds a new invocation
public void captureFrom(Object argument) {
// ...
this.arguments.add(argument);
// ...
}
// return the list of arguments, using raw types remove any compiler checks for validity,
// the returned List contains elements that are not of type T
public List<T> getAllValues() {
// ...
return new ArrayList<T>((List) arguments);
// ...
}
This should be solvable by changing org.mockito.ArgumentCaptor
in a way that it passes its Class<? extends T> clazz
into the CapturingMatcher
and therefore passing the type information along properly, enabling a proper matches
implementation and removing the need for the cast / raw type usage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With