Mockito失败了"争论是不同的"但调试器的显示方式不同

时间:2016-04-27 19:13:28

标签: java mockito

我正在测试一个使用Mockito模拟对象的方法。当我运行测试时,Mockito告诉我传递给参数的参数与预期的不同。如果我在我正在测试的方法中设置断点,那么调用它的参数实际上就是我所期望的。此方法仅被调用一次。为什么Mockito报告的内容与我在调试器中看到的内容有所不同,我该如何解决这个问题呢?

以下是测试:

@Test
public void addExclusivePermutationsTest() {
    Permutation p1 = mock(Permutation.class);

    PermutationBuilder pb = new PermutationBuilder();

    when(p1.applyPermutations(anySetOf(String.class)))
            .thenReturn(Collections.singleton("abc123"));

    pb.addExclusivePermutations(p1);
    pb.permute("test");

    verify(p1).applyPermutations(new HashSet<>(
            Collections.singletonList("test")));
}

测试非常简单。 Permutation是包含方法定义Set<String> applyPermutations(Set<String>)的接口。在p1上调用该方法时,我告诉Mockito返回包含[abc123]的集合。

addExclusivePermutations(p1)调用只会将p1添加到包含ExclusivePermutation个对象列表的Permutation对象。 ExclusivePermutationPermutation的实施者,因此其applyPermutations方法如下所示:

public Set<String> applyPermutations(final Set<String> stringPermutations) {
    Set<String> exclusivePermutations = new HashSet<>();

    for (Permutation permutation : permutationList) {
        exclusivePermutations.addAll(permutation.applyPermutations(stringPermutations));
    }

    return exclusivePermutations;
}

permute("test")内调用上述方法。字符串"test"将转换为Set,并传递给上述方法。上述方法是在applyPermutations上调用p1的方法。当我使用调试器查看此方法时,stringPermutations仅包含[test],正如我所期望的那样,并直接传递到p1。然后p1返回[abc123],因为它已被模拟,上述方法将其与stringPermutations连接以返回[test, abc123]。因此,出于某种原因,Mockito说调试器显示的stringPermutations不是[test],而是该方法返回的内容。

最后,这是错误:

Argument(s) are different! Wanted:
permutation.applyPermutations(
    [test]
);
-> at PermutationBuilderTest$AddExclusivePermutationTests.addExclusivePermutationsTest(PermutationBuilderTest.java:87)
Actual invocation has different arguments:
permutation.applyPermutations(
    [abc123, test]
);

最后一点(对于那些仍在阅读的人)。如果我通过复制ExclusivePermuation并将其直接放在ExclusivePermutation.applyPermutation()内来绕过PermutationBuilder,则测试通过。这是一个谜......

修改

所以我把它缩小了一点。在PermutationBuilder我有这个方法:

public Set<String> permute(String s) {
    for (Modifier modifier : modifierList) {
        s = modifier.applyModification(s);
    }

    Set<String> stringPermutations = new HashSet<>(Collections.singletonList(s));

    for (Permutation permutation : permutationList) {
        Set<String> result = permutation.applyPermutations(stringPermutations);
        stringPermutations.addAll(result);
    }

    return stringPermutations;
}

在我分配结果后,Mockito说该方法是使用[test]调用的。但是,当我走到下一行将其添加到stringPermutations时,它会更改为[abc123, test]。您可以在下面的两个屏幕截图中看到这一点(查看arguments数组的底部):

After getting result

enter image description here

2 个答案:

答案 0 :(得分:1)

问题在于您正在修改您用作参数的集合。

即代码

Set<String> stringPermutations = new HashSet<>(Collections.singletonList(s));

for (Permutation permutation : permutationList) {
    Set<String> result = permutation.applyPermutations(stringPermutations);
    stringPermutations.addAll(result);
}

您创建stringPermutations对象,并向其添加test。您现在拥有一个包含一个元素的集合。

然后,您拨打permutation.applyPermutations,并将结果添加到stringPermutations

stringPermutations现在包含test abc123

这里几乎可以肯定的是,Mockito 克隆参数(这将是......难以正确),它只是保留对对象的引用(s传入。

由于您在调用该方法后改变了stringPermutations ,因此Mockito认为您使用包含test {{1}的集合调用了该方法因为当它试图验证断言时,它就是集合中的内容。解决此问题的最简单方法是在将集合传递给方法后不要修改集合。

解决方案可能是:

abc123

如果确实需要Set<String> stringPermutations = new HashSet<>(); for (Permutation permutation : permutationList) { Set<String> result = permutation.applyPermutations(Collections.singletonList(s)); stringPermutations.addAll(result); } stringPermutations.addAll(Collections.singletonList(s)); 来包含这两个元素。

答案 1 :(得分:0)

mockito github中针对同一问题打开了两个问题:

因此,首先,创建者将其视为框架的局限性,因为在调用时复制参数会影响性能,而且仍然没有通用的方式进行深度复制。

第二,上面的语句解释了在这种情况下应采取的措施:您需要自己克隆参数。我没有尝试过,但是建议的方法如下:

  • org.mockito.internal.stubbing.answers.ClonesArguments
  • 在doAnswer中进行手动复制并验证它而不是捕获者

但是,当我在用例中使用ClonesArguments时:

doAnswer((invocation) -> {
    new ClonesArguments().answer(invocation);
    // in my case situation is different and instead of modifying invocation
    // my argument is the same collection 4 times with different elements
    // in your case it may or may not work
    // maybe you will have to copy paste and update their code
    return myAnswer;
})

我在调试中看到我的集合中的元素已被复制,但是在验证阶段,我仍然仅看到最新的值,因此对我不起作用。