如何验证是否使用 Mockito 调用了两种方法中的一种?
How can I verify that one of two methods was called using Mockito?
假设我有一个 class 有两个我不关心哪个方法叫做...
public class Foo {
public String getProperty(String key) {
return getProperty(key, null);
}
public String getProperty(String key, String defaultValue) {
//...
}
}
以下两项(来自另一个 class,仍在我的申请中)应该通过我的测试:
public void thisShouldPass(String key) {
// ...
String theValue = foo.getProperty(key, "blah");
// ...
}
public void thisShouldAlsoPass(String key) {
// ...
String theValue = foo.getProperty(key);
if (theValue == null) {
theValue = "blah";
}
// ...
}
我不在乎调用了哪个,我只想调用两个变体之一。
在 Mockito 中,我通常可以这样做:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
或者:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
有没有当地的说法"verify either one or the other occurred at least once"?
或者我必须做一些像这样粗鲁的事情:
try {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
} catch (AssertionError e) {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
}
在您的特定情况下,getProperty(String)
在内部调用 getProperty(String, String)
。
public String getProperty(String key) {
/*
* getProperty(String, String) is called anyway.
* Why not simply verify the occurrence of that?
*/
return getProperty(key, null);
}
简单地验证第二种方法等同于验证一种或另一种方法至少出现一次。
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
您可以将 atLeast(0)
与 ArgumentCaptor
结合使用:
ArgumentCaptor<String> propertyKeyCaptor = ArgumentCaptor.forClass(String.class);
Mockito.verify(foo, atLeast(0)).getProperty(propertyKeyCaptor.capture(), anyString());
ArgumentCaptor<String> propertyKeyCaptor2 = ArgumentCaptor.forClass(String.class);
Mockito.verify(foo, atLeast(0)).getProperty(propertyKeyCaptor2.capture());
List<String> propertyKeyValues = propertyKeyCaptor.getAllValues();
List<String> propertyKeyValues2 = propertyKeyCaptor2.getAllValues();
assertTrue(!propertyKeyValues.isEmpty() || !propertyKeyValues2.isEmpty()); //JUnit assert -- modify for whatever testing framework you're using
一般来说,如果您在任何类型的 "getter" 上调用 verify
,那么您对实现的假设太多了。 Mockito一般是为灵活测试而设计的(相对于"brittle"即使代码正确也需要更改的测试);你的测试应该更关心是否值是正确的,而不是哪个方法被用来获取那个值。更好的解决方案可能是 stub 两个 getter 到 return 一个可预测的值,然后对相同的值使用正常断言以确保它到达正确的位置。
when(mockFoo.getProperty("bar")).thenReturn("bar value");
when(mockFoo.getProperty("bar", anyString())).thenReturn("bar value");
// ...
assertEquals("bar value", new SystemUnderTest(mockFoo).getBarProperty());
Mockito 的文档说明了这一点:
Although it is possible to verify a stubbed invocation, usually it's just redundant. Let's say you've stubbed foo.bar()
. If your code cares what foo.bar()
returns then something else breaks (often before even verify()
gets executed). If your code doesn't care what get(0)
returns then it should not be stubbed.
就是说,如果这是您需要支持的模式(或具有重载和副作用的方法调用),您可以通过 Mockito.mockingDetails
and MockingDetails.getInvocations
获得大量信息,包括截至模拟 1.10.0。您需要遍历 Invocation 对象以检查多个方法。
boolean found = false;
Method method1 = Foo.class.getMethod("getProperty", String.class);
Method method2 = Foo.class.getMethod("getProperty", String.class, String.class);
for (Invocation invocation : Mockito.mockingDetails(foo).getInvocations()) {
if (method1.equals(invocation.getMethod())
|| method2.equals(invocation.getMethod()) {
found = true;
break;
}
}
assertTrue("getProperty was not invoked", found);
请注意,这个 second 解决方案有点危险,因为它没有受益于 IDE 内置的自动重构工具,并且可能比其他一些解决方案更难阅读。 (以上可能还缺少对 isIgnoredForVerification
、markVerified
和其他细节的调用。)但是,如果您预见到在大型代码库中经常需要这样做,那么使用 Mockito 的内置 API 可能会为您提供很多帮助比其他方式更灵活。
假设我有一个 class 有两个我不关心哪个方法叫做...
public class Foo {
public String getProperty(String key) {
return getProperty(key, null);
}
public String getProperty(String key, String defaultValue) {
//...
}
}
以下两项(来自另一个 class,仍在我的申请中)应该通过我的测试:
public void thisShouldPass(String key) {
// ...
String theValue = foo.getProperty(key, "blah");
// ...
}
public void thisShouldAlsoPass(String key) {
// ...
String theValue = foo.getProperty(key);
if (theValue == null) {
theValue = "blah";
}
// ...
}
我不在乎调用了哪个,我只想调用两个变体之一。
在 Mockito 中,我通常可以这样做:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
或者:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
有没有当地的说法"verify either one or the other occurred at least once"?
或者我必须做一些像这样粗鲁的事情:
try {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
} catch (AssertionError e) {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
}
在您的特定情况下,getProperty(String)
在内部调用 getProperty(String, String)
。
public String getProperty(String key) {
/*
* getProperty(String, String) is called anyway.
* Why not simply verify the occurrence of that?
*/
return getProperty(key, null);
}
简单地验证第二种方法等同于验证一种或另一种方法至少出现一次。
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
您可以将 atLeast(0)
与 ArgumentCaptor
结合使用:
ArgumentCaptor<String> propertyKeyCaptor = ArgumentCaptor.forClass(String.class);
Mockito.verify(foo, atLeast(0)).getProperty(propertyKeyCaptor.capture(), anyString());
ArgumentCaptor<String> propertyKeyCaptor2 = ArgumentCaptor.forClass(String.class);
Mockito.verify(foo, atLeast(0)).getProperty(propertyKeyCaptor2.capture());
List<String> propertyKeyValues = propertyKeyCaptor.getAllValues();
List<String> propertyKeyValues2 = propertyKeyCaptor2.getAllValues();
assertTrue(!propertyKeyValues.isEmpty() || !propertyKeyValues2.isEmpty()); //JUnit assert -- modify for whatever testing framework you're using
一般来说,如果您在任何类型的 "getter" 上调用 verify
,那么您对实现的假设太多了。 Mockito一般是为灵活测试而设计的(相对于"brittle"即使代码正确也需要更改的测试);你的测试应该更关心是否值是正确的,而不是哪个方法被用来获取那个值。更好的解决方案可能是 stub 两个 getter 到 return 一个可预测的值,然后对相同的值使用正常断言以确保它到达正确的位置。
when(mockFoo.getProperty("bar")).thenReturn("bar value");
when(mockFoo.getProperty("bar", anyString())).thenReturn("bar value");
// ...
assertEquals("bar value", new SystemUnderTest(mockFoo).getBarProperty());
Mockito 的文档说明了这一点:
Although it is possible to verify a stubbed invocation, usually it's just redundant. Let's say you've stubbed
foo.bar()
. If your code cares whatfoo.bar()
returns then something else breaks (often before evenverify()
gets executed). If your code doesn't care whatget(0)
returns then it should not be stubbed.
就是说,如果这是您需要支持的模式(或具有重载和副作用的方法调用),您可以通过 Mockito.mockingDetails
and MockingDetails.getInvocations
获得大量信息,包括截至模拟 1.10.0。您需要遍历 Invocation 对象以检查多个方法。
boolean found = false;
Method method1 = Foo.class.getMethod("getProperty", String.class);
Method method2 = Foo.class.getMethod("getProperty", String.class, String.class);
for (Invocation invocation : Mockito.mockingDetails(foo).getInvocations()) {
if (method1.equals(invocation.getMethod())
|| method2.equals(invocation.getMethod()) {
found = true;
break;
}
}
assertTrue("getProperty was not invoked", found);
请注意,这个 second 解决方案有点危险,因为它没有受益于 IDE 内置的自动重构工具,并且可能比其他一些解决方案更难阅读。 (以上可能还缺少对 isIgnoredForVerification
、markVerified
和其他细节的调用。)但是,如果您预见到在大型代码库中经常需要这样做,那么使用 Mockito 的内置 API 可能会为您提供很多帮助比其他方式更灵活。