简体   繁体   中英

How do I use ArgumentCaptor after ArgumentMatcher?

I have some common interface to mock:

public interface RequestHandler {
  public Object handle(Object o);
}

And this mocked interface should handle different requests in single test.

when(mock.handle(isA(ARequest.class))).thenReturn(new AResponse());
when(mock.handle(isA(BRequest.class))).thenReturn(new BResponse());

But I want to capture passed instance of BRequest to check all its params. Is it possible somehow?

For now I see only one solution: build a monstrous extension of ArgumentMatcher . However, I don't like this, because I will not see AssertionError messages.

Remember: Though matchers are for both stubs and verifications, ArgumentCaptor is for verifications only. That makes it simple:

ArgumentCaptor<BRequest> bCaptor = ArgumentCaptor.for(BRequest.class);
when(mock.handle(isA(BRequest.class))).thenReturn(new BResponse());

systemUnderTest.handle(createBRequest());

verify(mock).handle(bCaptor.capture());
BRequest bRequest = bCaptor.getValue();
// your assertions here

Note, however, that that also means you can't use ArgumentCaptor to select the response. That's where partial mocks or Answers come in:

when(mock.handle(any())).thenAnswer(new Answer<Object>() {
  @Override public Object answer(InvocationOnMock invocation) {
    Object argument = invocation.getArguments()[0];
    // your assertions here, and you can return your desired value
  }
});

Should you choose an ArgumentMatcher, it likely won't be all that monstrous, especially if you skip a factory method and let Mockito's de-camel-casing be your description:

public static class IsAnAppropriateBRequest extends ArgumentMatcher<Object> {
  @Override public boolean matches(Object object) {
    if !(object instanceof BRequest) {
      return false;
    }
    BRequest bRequest = (BRequest) object;
    // your assertions here
  }
}

when(mock.handle(argThat(new IsAnAppropriateBRequest())))
    .thenReturn(new BResponse());

Okay, first of all, the fact that this method can take so many different types of parameters is a code smell. A method that returns many different kinds of responses for different input parameters is a clear violation of this. See One Thing: Extract till you Drop.

For years authors and consultants (like me) have been telling us that functions should do one thing. They should do it well. They should do it only.

That said, you can pass a Partial Mock into your class. See: Real Partial Mocks

Example:

BRequest bSpy = spy(new BRequest());
when(mock.handle(bSpy))).thenReturn(new BResponse());
verify(bSpy, times(2)).someMethod();

bSpy won't return null, because it's a real instance of a BRequest , but because it's a spy, it allows you to call things like verify on it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM