我正在测试一个使用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
对象。 ExclusivePermutation
是Permutation
的实施者,因此其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
数组的底部):
答案 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中针对同一问题打开了两个问题:
因此,首先,创建者将其视为框架的局限性,因为在调用时复制参数会影响性能,而且仍然没有通用的方式进行深度复制。
第二,上面的语句解释了在这种情况下应采取的措施:您需要自己克隆参数。我没有尝试过,但是建议的方法如下:
但是,当我在用例中使用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;
})
我在调试中看到我的集合中的元素已被复制,但是在验证阶段,我仍然仅看到最新的值,因此对我不起作用。