'Mockito return value based on property of a parameter

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");
}


Solution 1:[1]

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 2:[2]

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

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

Solution 3:[3]

Yes you can, using a custom argument matcher.

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

Solution 4:[4]

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:[5]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dawood ibn Kareem
Solution 2
Solution 3 Ellen Spertus
Solution 4 tasomaniac
Solution 5 Ellen Spertus