Mockito return value based on property of a parameter

JavaMockito

Java Problem Overview


Normally when using Mockito I will do something like:

Mockito.when(myObject.myFunction(myParameter)).thenReturn(myResult);

Is it possible to do something along the lines of

myParameter.setProperty("value");
Mockito.when(myObject.myFunction(myParameter)).thenReturn("myResult");

myParameter.setProperty("otherValue");
Mockito.when(myObject.myFunction(myParameter)).thenReturn("otherResult");

So rather than when just using the parameter to determine the result. It is using a value of a property inside the parameter to determine the result.

So when the code is executed it behaves like so:

public void myTestMethod(MyParameter myParameter,MyObject myObject){
    myParameter.setProperty("value");
    System.out.println(myObject.myFunction(myParameter));// outputs myResult

    myParameter.setProperty("otherValue");
    System.out.println(myObject.myFunction(myParameter));// outputs otherResult
}

Here is the current solution, hopefully something better can be suggested.

private class MyObjectMatcher extends ArgumentMatcher<MyObject> {

    private final String compareValue;

    public ApplicationContextMatcher(String compareValue) {
        this.compareValue= compareValue;
    }

    @Override
    public boolean matches(Object argument) {
        MyObject item= (MyObject) argument;
        if(compareValue!= null){
            if (item != null) {
                return compareValue.equals(item.getMyParameter());
            }
        }else {
            return item == null || item.getMyParameter() == null;
        }
        return false;
    }
}

public void initMock(MyObject myObject){
    MyObjectMatcher valueMatcher = new MyObjectMatcher("value");
    MyObjectMatcher otherValueMatcher = new MyObjectMatcher("otherValue");
    Mockito.when(myObject.myFunction(Matchers.argThat(valueMatcher))).thenReturn("myResult");
    Mockito.when(myObject.myFunction(Matchers.argThat(otherValueMatcher))).thenReturn("otherResult");
}

Java Solutions


Solution 1 - Java

In Java 8 it is even simpler than all of the above:

when(mockObject.myMethod(anyString()))
    .thenAnswer(invocation -> 
        invocation.getArgumentAt(0, String.class));

Solution 2 - Java

Here's one way of doing it. This uses an Answer object to check the value of the property.

@RunWith(MockitoJUnitRunner.class)
public class MyTestClass {
    private String theProperty;
    @Mock private MyClass mockObject;
    
    @Before
    public void setUp() {
        when(mockObject.myMethod(anyString())).thenAnswer(
            new Answer<String>(){
            @Override
            public String answer(InvocationOnMock invocation){
                if ("value".equals(theProperty)){
                    return "result";
                }
                else if("otherValue".equals(theProperty)) {
                    return "otherResult";
                }
                return theProperty;
            }});
    }
}

There's an alternative syntax, which I actually prefer, which will achieve exactly the same thing. Over to you which one of these you choose. This is just the setUp method - the rest of the test class should be the same as above.

@Before
public void setUp() {
    doAnswer(new Answer<String>(){
        @Override
        public String answer(InvocationOnMock invocation){
            if ("value".equals(theProperty)){
                return "result";
            }
            else if("otherValue".equals(theProperty)) {
                return "otherResult";
            }
            return theProperty;
        }}).when(mockObject).myMethod(anyString());
}

Solution 3 - Java

Yes you can, using a custom argument matcher.

See the javadoc of Matchers for more details, and more specifically ArgumentMatcher.

Solution 4 - Java

Here is how it would look like in Kotlin with mockito-kotlin library.

mock<Resources> {
    on {
        mockObject.myMethod(any())
    } doAnswer {
        "Here is the value: ${it.arguments[0]}"
    }
}

Solution 5 - Java

You can do this with Mockito 3.6.0:

when(mockObject.myMethod(anyString()))
    .thenAnswer(invocation -> myStringMethod(invocation.getArgument(0)));

This answer is based on Sven's answer and Martijn Hiemstra's comment, with getArgumentAt() changed to getArgument().

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBevynQView Question on Stackoverflow
Solution 1 - JavaSvenView Answer on Stackoverflow
Solution 2 - JavaDawood ibn KareemView Answer on Stackoverflow
Solution 3 - JavafgeView Answer on Stackoverflow
Solution 4 - JavatasomaniacView Answer on Stackoverflow
Solution 5 - JavaEllen SpertusView Answer on Stackoverflow