java - Mockito - 使用 Varargs 参数模拟重载方法

标签 java unit-testing junit mockito

当我们需要模拟重载方法(其中一个方法使用可变参数)时,Mockito 很难使用。考虑 Spring 的 RestTemplate

中的以下方法
void put(String url, Object request, Object... uriVariables) throws RestClientException;

void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;

模拟第二个方法是直接的,但是模拟第一个方法是不可能的,因为使用 any() 会导致匹配这两个方法的不明确的方法调用,并且没有其他选择可以仅匹配对象...

以问答形式分享我经过一番努力后得到的解决方案,以帮助那些有相同想法的人。欢迎所有其他替代方案。

最佳答案

可以通过利用该功能向模拟提供 defaultAnswer 来尝试解决此问题。 defaultAnswer 将评估该调用是针对特定方法并执行所需的操作,如果所需的方法不是目标,则让调用遵循自然流程。

这可以通过一个例子很好地解释。考虑下面类中的两个重载方法:

public class StringConcat {
    public String concatenate(int i, String... strings) {
        return i + Arrays.stream(strings).collect(Collectors.joining(","));
    }

    public String concatenate(int i, List<String> strings) {
        return i + strings.stream().collect(Collectors.joining(","));
    }
}

可以使用 Mockito 模拟第二种方法,如下所示:

StringConcat stringConcat = mock(StringConcat.class);
when(stringConcat.concatenate(anyInt(), anyList())).thenReturn("hardcoded value");

为了表示可变参数,我们没有 anyVararg() 方法(已弃用且不起作用,不确定它在旧版本中是否有效)。但同样可以通过使用 defaultAnswer 创建模拟来处理,如下所示:

@Test
void testWithDefaultAnswer(){
    // Creating mock object with default answer
    StringConcat stringConcat = mock(StringConcat.class, invocation -> {
        Method method = invocation.getMethod();
        if (method.getName().contains("concatenate") && 
               method.getParameters()[method.getParameters().length-1].isVarArgs()){
            if(invocation.getArguments().length>=method.getParameterCount()){
                List varArgParams = Arrays.stream(invocation.getArguments())
                          .skip(method.getParameterCount()-1)
                          .collect(Collectors.toList());
                return invocation.getArguments()[0]+":"
                      +varArgParams.toString(); // mocked result when varargs provided
            }
            return ""+invocation.getArguments()[0]; // mocked result when varargs not provided
        }
        return Answers.RETURNS_DEFAULTS.answer(invocation); // Ensures seamless mocking of any other methods
    });

    // Mock any non varargs methods as needed
    when(stringConcat.concatenate(anyInt(), anyList())).thenReturn("hardcoded"); // mocking as usual

    // Test the mocks
    System.out.println(stringConcat.concatenate(1, "a", "b")); // default answer based mock, with varargs provided
    System.out.println(stringConcat.concatenate(1)); // default answer based mock, without varargs provided
    System.out.println(stringConcat.concatenate(1, Lists.newArrayList("a", "b"))); // mocked non varargs method
}

输出:

1:[a, b]
1
hardcoded

关于java - Mockito - 使用 Varargs 参数模拟重载方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55379745/

相关文章:

java访问修饰符

java - 谁能解释一下什么是状态和可变数据?

python - 模拟 : assert mock_calls with a numpy array as argument raises ValueError and np. testing.assert_array_equal 不起作用

java - 使用 Junit 进行多步测试

java - 无法使用 PowerMockito 测试 URL

java - TreeMap节点分配后为空

c++ - 单元测试 .cpp 操作方法

c# - 验证传递给 Mock 的参数是否按预期设置的正确方法

spring - 如何使用 Spring 的 RestTemplate 模拟 curl REST 调用?

java - 在Java中将链表添加到另一个链表的末尾