76

I have a rather complex java function that I want to test using jUnit and I am using Mockito for this purpose. This function looks something like this:

public void myFunction (Object parameter){
   ...
   doStuff();
   ...
   convert(input,output);
   ...
   parameter.setInformationFrom(output);
}

The function convert sets the attributes of output depending on input and it's a void type function, although the "output" parameter is what is being used as if it were returned by the function. This convert function is what I want to mock up as I don't need to depend on the input for the test, but I don't know how to do this, as I am not very familiar with Mockito.

I have seen basic cases as when(something).thenReturn(somethingElse) or the doAnswer method which I understand is similar to the previous one but more logic can be added to it, but I don't think these cases are appropriate for my case, as my function does not have a return statement.

Michu93
  • 5,058
  • 7
  • 47
  • 80
Juan
  • 1,754
  • 1
  • 14
  • 22
  • And what do you want to mock up? The output? – fge Dec 11 '14 at 11:16
  • 1
    Mockito aside, are you aware that writing `parameter=output;` won't change the value of the variable that you pass to this method? – Dawood ibn Kareem Dec 11 '14 at 11:27
  • @David Wallace, if parameter is "a", I call the real function myFunction and the I print parameter, then parameter will be "ab", for instance. It is not exactly "parameter=output", I just wrote that to understand the code. Sorry for the confusion. – Juan Dec 11 '14 at 11:43
  • @fge, yes I want set a value to output. – Juan Dec 11 '14 at 11:43
  • If I understand you correctly, you want to control what value is passed to `setInformationFrom`? You want to mock it so you can control the value of `output`? If so we need to know how the value of `output` is set. Is it retrieved by a method that can be mocked? – John B Dec 11 '14 at 16:01
  • Seems that you're using words *"input"* and *"parameter"* interchangeably, `output` appears out of nowhere... It would be useful to see a more concrete example that's closer to your real code because the snippet doesn't seem to match your description. One general suggestion would be to use stateless functions that return the output instead of modifying input parameters in-place - this is done precisely to facilitate testing. – kryger Dec 11 '14 at 16:43
  • How is the value of `output` assigned in your method? Helping you would be less of a challenge if you would show us the whole method, not just a couple of lines of it. – Dawood ibn Kareem Dec 11 '14 at 19:34
  • @JohnB that is correct. 'output' is created as an empty object with all its values set to null. 'convert' then uses information read from 'input' (which is set via spring) to populate 'output'. Then, some modifications are performed to 'output' and the most of its attributes get copied to parameter right before ending the function code – Juan Dec 11 '14 at 21:06
  • 1
    If you want the mocked method to call a method on (or otherwise alter) a parameter, you'll need to write an Answer as in this question (["How to mock a void return method affecting an object"](http://stackoverflow.com/q/12429169/1426891)). Hope this helps! – Jeff Bowman Dec 11 '14 at 21:06
  • @kryger I agree that is better to use functions to return the output, but I am creating it inheriting from a very similar one so I should keep it that way. Input is a variable whose value is set via spring. Output is declared as null right before the convert function is called, 'convert' just populates it. – Juan Dec 11 '14 at 21:11
  • @JeffBowman perfect! I think that is what I was looking for, thank you. I don't think I managed to explain myself well enough though. – Juan Dec 11 '14 at 21:15

1 Answers1

121

If you want the mocked method to call a method on (or otherwise alter) a parameter, you'll need to write an Answer as in this question ("How to mock a void return method affecting an object").

From Kevin Welker's answer there:

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

Note that newer best-practices would have a type parameter for Answer, as in Answer<Void>, and that Java 8's lambdas can compress the syntax further. For example:

doAnswer(invocation -> {
  Object[] args = invocation.getArguments();
  ((MyClass)args[0]).myClassSetMyField(NEW_VALUE);
  return null; // void method in a block-style lambda, so return null
}).when(mock).someMethod();
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • 1
    you can change new Answer() to be of type Void and also change the the return type of answer() to Void to avoid a warning on the constructor. – Moz Aug 09 '17 at 19:50
  • You can also use shorthand in lambda – `MyClass arg0 = invocation.getArgument(0);` as `getArgument(int)` method takes in `` and return the type `T`. – stephen Jan 15 '19 at 03:51
  • @aheryan Yes, provided you're on [Mockito 2.1 or better](https://static.javadoc.io/org.mockito/mockito-core/2.7.9/org/mockito/invocation/InvocationOnMock.html#getArgument(int)) and Java 8 or better (because type inference on return values is new in Java 8). Granted those have been around for a few years now, but they weren't when this answer was written in 2014. – Jeff Bowman Jan 15 '19 at 07:19