英文:
Why does Mockito throw up when accessing a mock while verifying another one?
问题
以下是翻译好的内容:
查看这个示例:
class Foo { }
class Bar {
void takeIt(int i, String arg) { System.out.println(arg + i); }
}
public class Mcve {
@Test
public void passes() {
Foo foo = Mockito.mock(Foo.class);
Bar bar = Mockito.mock(Bar.class);
bar.takeIt(42, "surprise: " + foo);
String substring = "surprise: " + foo;
Mockito.verify(bar).takeIt(ArgumentMatchers.eq(42),
ArgumentMatchers.contains(substring));
}
@Test
public void fails() {
Foo foo = Mockito.mock(Foo.class);
Bar bar = Mockito.mock(Bar.class);
bar.takeIt(42, "surprise: " + foo);
Mockito.verify(bar).takeIt(ArgumentMatchers.eq(42),
ArgumentMatchers.contains("surprise: " + foo));
}
}
这两个测试几乎完全相同,唯一的区别是用于 contains()
匹配器的字符串在 passes()
中提前计算,但在 fails()
中是内联的。
fails()
抛出异常:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at com.ibm.hwmca.z.svm.zhyp.managed.Mcve.fails(Mcve.java:43)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at Mcve.fails(Mcve.java:43)
...
(显然,错误信息是错误的,因为上面的代码对所有参数都使用了匹配器)
更有趣的是:仅在需要匹配超过一个参数时才会失败(如果从 gimme()
中删除 int
参数,并且只传递/匹配该字符串参数:pass)。
有人能解释一下这里到底发生了什么,并且是否有一种方法可以进行这样的匹配,比如 contains("surprise: " + foo)
,其中 foo
是由 Mockito 模拟的吗?
当然,这实际上是一个 MCVE(最小可复现示例)。我在我们的环境中花了3个小时,从失败的单元测试得到了上面的示例。
在实际环境中,Bar 类是一个模拟的日志记录工具。
而 Foo 对象表示由某个虚拟持久层创建的某种“数据实体”。我必须验证生产代码是否记录了特定的信息,其中一些信息是从虚拟数据对象中派生出来的。
英文:
See this example:
class Foo { }
class Bar {
void takeIt(int i, String arg) { System.out.println(arg + i); }
}
public class Mcve {
@Test
public void passes() {
Foo foo = Mockito.mock(Foo.class);
Bar bar = Mockito.mock(Bar.class);
bar.takeIt(42, "surprise: " + foo);
String substring = "surprise: " + foo;
Mockito.verify(bar).takeIt(ArgumentMatchers.eq(42),
ArgumentMatchers.contains(substring));
}
@Test
public void fails() {
Foo foo = Mockito.mock(Foo.class);
Bar bar = Mockito.mock(Bar.class);
bar.takeIt(42, "surprise: " + foo);
Mockito.verify(bar).takeIt(ArgumentMatchers.eq(42),
ArgumentMatchers.contains("surprise: " + foo));
}
}
The two tests are almost identical, the only difference: the string used for the contains()
matcher is computed upfront in passes()
, but inlined in fails()
.
fails()
throws up:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at com.ibm.hwmca.z.svm.zhyp.managed.Mcve.fails(Mcve.java:43)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at Mcve.fails(Mcve.java:43)
...
(Obviously: the error message is plain wrong, as the above code is using a matcher for all parameters)
Even more interesting: it only fails with more than 1 argument to match on (if one removes the int
parameter from gimme()
, and just passes/matches that string argument: pass).
Can anyone explain exactly what is happening here, and is there a way to do such a matching like contains("surprise: " + foo)
, with foo
being something Mockito-mocked?
Of course, this is really meant as MCVE. It took me 3 hours to get from the failing unit test in our environment to this example here.
In the real environment, the Bar class is a mocked logging facility.
And the Foo object represents some "data entity" that gets created by some fake persistence layer. I have to verify that the production code does log specific information, and some of that information is derived from the faked data objects.
专注分享java语言的经验与见解,让所有开发者获益!
评论