【问题标题】:mockito return value based on property of a parameter基于参数属性的模拟返回值
【发布时间】:2026-02-03 00:50:02
【问题描述】:

通常在使用 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");
}

【问题讨论】:

  • 从你的问题中不清楚要模拟的对象是什么,被测对象是什么。
  • 我添加了一个编辑来演示我希望它的行为方式。
  • 无论以何种方式阅读本文,在我看来,您都在嘲笑您要测试的对象。除非你有令人信服的理由这样做,并且你这样做的方式不会妨碍对象的原始行为,否则首先会违背测试的目的。
  • 是的,@ylabidi 确实有道理。您想做的事情是可能的,但它“感觉”或“闻起来”有点尴尬。你应该退后一步,问问这是否以及为什么真的有必要。这就是测试的原因之一:糟糕的设计通常会导致测试类变得困难或尴尬。
  • 如果您想提出更好的建议,您不应该接受答案。一旦你接受了一个答案,大多数人就不会费心提供一个不同的答案。对于它的价值,我相信有更好的方法 - 我可能会稍后发布答案。

标签: java mockito


【解决方案1】:

在 Java 8 中,它甚至比上述所有方法都简单:

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

【讨论】:

  • 我喜欢这个想法,但它似乎不完整?这如何区分“value”和“otherValue”并在每种情况下返回不同的答案?
  • 您可以通过使 lambda 更大来区分不同的参数。只需添加一个 if 来选择写入。可以是这样的:invocation-&gt; { String param = invocation.getArgumentAt(0, String.class); if (param.equals("value")) return "myResultOnValue"; if (param.equals("otherParam") return "myResultOnOtherValue" else return "MyDefaultResult";
  • 不再需要定义类型。这更容易: when(mockObject.myMethod (anyString() ) ).thenAnswer(invocation -> invocation.getArgument(0));
  • Sven 和@MartijnHiemstra,您的回答对我很有帮助。谢谢!我不确定是要编辑这个答案还是创建一个新答案。随意将我的任何有用的东西复制到你的,斯文,我可以删除我的。 *.com/a/64880543/631051
【解决方案2】:

这是一种方法。这使用Answer 对象来检查属性的值。

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

还有一种我更喜欢的替代语法,它可以实现完全相同的效果。由你选择其中的哪一个。这只是setUp 方法 - 测试类的其余部分应该与上面相同。

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

【讨论】:

  • 您提供的解决方案不允许根据模拟方法的不同参数模拟响应。在这种情况下,如果我们想根据 mockObject.myMethod(String argument) 的参数返回不同的响应;如果在测试的运行时,如果“argument”是“value”然后返回这个,如果“argument”是“othervalue”然后返回别的东西。
  • 要获取参数,您可以执行类似Object[] args = invocation.getArguments(); return (String) args[0];
【解决方案3】:

可以,使用自定义参数匹配器。

请参阅the javadoc of Matchers 了解更多详情,更具体地说,请参阅ArgumentMatcher

【讨论】:

  • 我可以在“when”方法中使用它。 Mockito.argThat 返回 null,从而导致异常。
  • @TT。有人修复了链接。我进一步更新了它们以始终指向最新版本。
【解决方案4】:

这是在 Kotlin 中使用 mockito-kotlin 库的样子。

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

【讨论】:

    【解决方案5】:

    您可以使用 Mockito 3.6.0 做到这一点:

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

    此答案基于Sven's answer 和 Martijn Hiemstra 的评论,将getArgumentAt() 更改为getArgument()

    【讨论】:

    • 在 Mockito 3.6.0 中,你也可以这样做:when(mockObject.myMethod(anyString())) .thenAnswer(AdditionalAnswers.returnsFirstArg());
    最近更新 更多