0

How to capture the Class argument of a method with Argument Capture For the following method

  public <T> T response(RequestContent requestContent, Class<T> returnType) throws Exception
  {
    var response = getResponse(requestContent);
    var type = objectMapper.getTypeFactory().constructType(returnType);
    return objectMapper.readValue(response, type);
  }

The method is used in the caller class as

var res = response(requestContent, Account.class);

I do not know how to use doReturn and Argument Capture in the following situations:

doReturn(Account).when(stub).response(isA(RequestContent.class), ???);

and

verify(stub).getResponseClass(requestCaptor.capture(), ???);

I get the following error

No argument value was captured!
You might have forgotten to use argument.capture() in verify()...
...or you used capture() in stubbing but stubbed method was not called.
Be aware that it is recommended to use capture() only with verify()
Karos
  • 51
  • 4

1 Answers1

1

"No argument value was captured!" usually comes because you didn't invoke the expected method in your system under test. If you want to use a Captor for the sake of learning how, you'll need to first stub your change, then invoke your system under test, then verify.

I'm assuming here that you're trying to extract the request content so you can check it was a reasonable request.

Account account = setUpTheAccountYouWantToReturnHere();

doReturn(account)
    .when(stub)
    .response(isA(RequestContent.class), any(Class.class));  // or any()

systemUnderTest.doThingYouAreTestingThatInvokesTheStubResponse();

// You can also set this up using a @Captor annotation.
ArgumentCaptor<RequestContent> captor =
    ArgumentCaptor.forClass(RequestContent.class);

verify(stub).response(captor.capture(), any(Class.class));

// Now that your captor has captured a value, you can start to assert against it.

checkThatRequestContentPropertiesAreCorrect(captor.getValue());

I've used any(Class.class) on the assumption that it's not important, but you can also use eq(Account.class) instead of any(Class.class). This has the added benefit of failing your test (because you won't have a matching stub) if your system under test calls response with anything other than an Account as its second parameter.

For that matter, if your RequestContent supports equals and hashCode based on its contents, you can confirm that it has the right values by creating the RequestContent you expect to see and using eq(yourExpectedRequestContent) in your when call in Mockito. However, depending on the size of RequestContent and how reasonable its toString() implementation is, it might be easier to just call capture and check its properties as above.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thank you Jeff, I get it working with any(),. The only problem I could not get it working with any(CustomClass.class). I am using Junit 5 and latest Mockito version. it gets the following error reason: `Required type: Class - no instance(s) of type variable(s) T exist so that SubAccount conforms to Class`. The second parameter of the class is not an object but Class as I described – Karos Jun 02 '23 at 14:50
  • `any(T.class)` stands in for an instance of type T, so my `any(Class.class)` works because you're trying to check against an instance of type Class, namely the same one as `CustomClass.class`. The way you have it you would be checking that the final parameter is an instance of CustomClass (SubAccount), not the Class object itself. Therefore, if you want to check that the instance is equal to that, you have to use `eq(CustomClass.class)`. My text refers to `eq(Account.class)` as another example. – Jeff Bowman Jun 02 '23 at 17:55