基于参数属性的 Mockito 返回值

通常在使用 Mockito 时,我会这样做:

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

有没有可能做一些类似于

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


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

因此,与其仅仅使用参数来确定结果。它使用参数内部属性的值来确定结果。

因此,当代码被执行时,它的行为是这样的:

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
}

下面是目前的解决方案,希望能有更好的建议。

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");
}
99919 次浏览

Yes you can, using a custom argument matcher.

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

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

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

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

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]}"
}
}

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