Search code examples
javapolymorphismmockito

Using Mockito's ArgumentCaptor class to match a child class


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));

Solution

  • You can also use Mockito.isA to verify that the argument is of a specific class:

    verify(mock).init(isA(ExpectedClass.class));
    

    Mockito JavaDoc