24

I am writing unit tests for my application and I was wondering if it is possible for the Mockito framework to affect an object that is passed into a method that returns void of a mocked class. For instance, calling a mocked validation class that contains a method that returns void but tracks various changes and metadata via an object passed in as an argument. .

public GetCartItemsOutput getCartItems(GetCartItemsInput getCartItemsInput) {
    CartItemsFilter cartItemsFilter = new CartItemsFilter();
    validator.validateCartItemsInput(getCartItemsInput, cartItemsFilter); ...

I mocked the validator class for my other tests but for this one I need mock the changes to the cartItemsFilter object which I do not know how to do.

tamuren
  • 1,072
  • 4
  • 15
  • 32

3 Answers3

42

The answer is yes, you can, and there are basically two levels of doing this, based on the need of your test.

If you merely want to test the interaction with the mocked object, you can simply use the verify() method, to verify that the void method was called.

If your test genuinely needs the mocked object to modify parameters passed to it, you will need to implement an Answer:

EDITED to show proper form of using Answer with void method

doAnswer(new Answer() {
    @Override
    Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        ((MyClass)args[0]).myClassSetMyField(NEW_VALUE);
        return null; // void method, so return null
    }
}).when(mock).someMethod();

In Java 8+, the above is simplified with a lambda:

doAnswer(invocation-> {
    Object[] args = invocation.getArguments();
    ((MyClass)args[0]).myClassSetMyField(NEW_VALUE);
    return null;
}).when(mock).someMethod();
Kevin Welker
  • 7,719
  • 1
  • 40
  • 56
  • 1
    Thanks for your help. Took me a while to wrap my head around "((MyClass)args[0]).myClassSetMyField(NEW_VALUE);", but eventually I figured out the point was to set the parameters of the argument I passed into my mocked method. Again, ty. – tamuren Sep 20 '12 at 13:33
5

To add to Kevin Welker's answer, if your MyClass is a custom class, then define a equals/hashcode method in it, so that Mockito can use it to match to the method call exactly.

In my case, "MyClass" was in a third-party API, so I had to use the method "any" as below -

 doAnswer(new Answer() {
    Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        ((MyClass)args[0]).myClassSetMyField(NEW_VALUE);
        return null; // void method, so return null
    }
}).when(mock).someMethod(any(MyClass.class));
Jay Khatwani
  • 389
  • 4
  • 6
-1

ArgumentCaptor might also help.

Here's a snippet found from here:

  ArgumentCaptor<GetCartItemsInput > argument = ArgumentCaptor.forClass(GetCartItemsInput .class);
  verify(mock).getCartItems(argument.capture());
  assertEquals(cartItemsFilter, argument.getValue());
Partha Mishra
  • 304
  • 3
  • 10