我有一块测试代码试图在通用情况下在后续调用中返回两个值,但在特定情况下只返回与该情况相关的值。代码看起来像:
ImportError Traceback (most recent call last)
/home/usr/script.py in <module>()
1 import pymc
----> 2 from MCMC2 import ExoData
...
/home/usr/MCMC2/__init__.py in <module>()
...
----> 4 import MCMC2.main_script
...
/home/usr/MCMC2/main_script.py in <module>()
1 try: from Models import model_main
----> 2 except: from .Models import model_main
...
/home/usr/MCMC2/Models/__init__.py in <module>()
----> 1 import Models.model_main
...
ImportError: No module named 'Models'
预期的行为是,在调用when(mockObject.method(anyString())).thenReturn(string1, string2);
when(mockObject.method(eq("expectedInput1"))).thenReturn(string1);
when(mockObject.method(eq("expectedInput2"))).thenReturn(string2);
和mockObject.method("foo")
时,应分别返回mockObject.method("bar")
和string1
,但测试实际上看到{{1}的两个响应}}。这是string2
中的错误吗?或者我误解了string2
模式匹配。
我的假设是匹配的最后一个模式是返回的内容,但是当经历该过程时,Mockito
是否分别处理第一个Mockito
块中的每个参数?有没有办法绕过这种行为?
当我在调用时注释掉后两个时,模拟的行为与预期一致,所以我假设有一些关于重叠匹配器行为的特定内容。
修改:这是Mockito
版本thenReturn
答案 0 :(得分:10)
我今天遇到了这个问题。这是由于调用mock来设置实际消耗已存在的存根的存根。
在此示例中,将第一行更改为
when(mock.call(anyString())).thenReturn("","",string1,string2)
当您设置其他模拟返回时,这将给您两个空白响应,将string1作为第一个有用的返回值。
还尝试doReturn替代方案,我认为可能没有这些问题:
doReturn(string1,string2).when(mock).call(anyString());
这会在设置过程中以不同方式使用存根。
所以我对此做了更多的研究。根据OP的问题,这是我正在玩的功能:
Function<String, String> function = mock(Function.class);
when(function.apply(anyString())).thenReturn("A","B","C");
when(function.apply("Jim")).thenReturn("Jim");
when(function.apply("Bob")).thenReturn("Bob");
assertThat(function.apply("Jim")).isEqualTo("Jim");
assertThat(function.apply("Bob")).isEqualTo("Bob");
assertThat(function.apply("")).isEqualTo("A");
assertThat(function.apply("")).isEqualTo("B");
assertThat(function.apply("")).isEqualTo("C");
assertThat(function.apply("")).isEqualTo("C");
上述内容在isEqualTo("A")
失败,因为设置Jim
和Bob
的模拟的两次调用会消耗提供给anyString()
的列表中的返回值。
您可能很想重新排序when
子句,但这会失败,因为anyString()
会取代特殊情况,因此也会失败。
以上版本的上述DOES按预期工作:
when(function.apply(anyString())).thenReturn("A","B","C");
doReturn("Jim")
.when(function)
.apply("Jim");
doReturn("Bob")
.when(function)
.apply("Bob");
assertThat(function.apply("Jim")).isEqualTo("Jim");
assertThat(function.apply("Bob")).isEqualTo("Bob");
assertThat(function.apply("")).isEqualTo("A");
assertThat(function.apply("")).isEqualTo("B");
assertThat(function.apply("")).isEqualTo("C");
assertThat(function.apply("")).isEqualTo("C");
这是因为doReturn
技术用于修改飞行中预先存在的模拟,实际上并不涉及在模拟上调用方法来设置模拟。
您可以使用doReturn
进行所有设置,而不是在when
... thenReturn
和doReturn
之间混合.. when
.. {{1 }}。碰巧,这有点丑陋:
function()
没有方便的 doReturn("A").doReturn("B").doReturn("C")
.when(function)
.apply(anyString());
功能让您按顺序指定多个返回。以上内容已经过测试,确实有效。
答案 1 :(得分:2)
解决此问题的一种方法是使用正则表达式以避免重叠:
when(mockObject.method(eq("expectedInput1"))).thenReturn(string1);
when(mockObject.method(eq("expectedInput2"))).thenReturn(string2);
// Match with any input string that doesn't contain expectedInput1 neither expectedInput2
when(mockObject.method(matches("((?!expectedInput1|expectedInput2).)*")))
.thenReturn(string1, string2);
示例:的
System.out.println("expectedInput1=" + mockObject.method("expectedInput1"));
System.out.println("expectedInput2=" + mockObject.method("expectedInput2"));
System.out.println("foo=" + mockObject.method("foo"));
System.out.println("bar=" + mockObject.method("bar"));
System.out.println("bar=" + mockObject.method("bar"));
输出:
expectedInput1=string1
expectedInput2=string2
foo=string1
bar=string2
bar=string2
另一种方法是实施ArgumentMatcher
仍然可以避免重叠:
when(mockObject.method(eq("expectedInput1"))).thenReturn(string1);
when(mockObject.method(eq("expectedInput2"))).thenReturn(string2);
when(
mockObject.method(
argThat(
new ArgumentMatcher<String>(){
@Override
public boolean matches(final Object argument) {
return !"expectedInput1".equals(argument)
&& !"expectedInput2".equals(argument);
}
}
)
)
).thenReturn(string1, string2);
另一种方法是使用以下内容实现Answer
:
when(mockObject.method(anyString())).thenAnswer(
new Answer<String>() {
Iterator<String> it = Arrays.asList(string1, string2).iterator();
String result;
@Override
public String answer(final InvocationOnMock invocation) throws Throwable {
String argument = (String) invocation.getArguments()[0];
switch (argument) {
case "expectedInput1" :
return string1;
case "expectedInput2" :
return string2;
default:
if (it.hasNext()) {
result = it.next();
}
return result;
}
}
}
);
答案 2 :(得分:2)
很难说这是一个错误还是一个功能......问题是,当你致电mockObject.method(eq("expectedInput1"))
执行第二次存根时,第一个存根已经到位。所以此调用返回string1
,然后无用地传递给when
。后续调用返回string2
,其中包括在实际测试期间调用最后一个存根和稍后的调用。
我几乎看不到任何优雅的方式,除了使用像@Nicolas建议的自定义Answer
之外,虽然它看起来像是一种矫枉过正。或许,您可以使用自定义匹配器而不是anyString()
,这实际上会说“任何字符串除了那两个”。这样你就不会有一个匹配器与另一个匹配器。
P上。 S.现在@Nicolas编辑了他的答案,正则表达看起来就像我的意思。除此之外,您根本不需要实现自定义匹配器。