如何验证使用Mockito调用两种方法之一?

Kid*_*rla 15 java mockito

假设我有一个有两种方法的类,我不关心哪种被称为...

public class Foo {
    public String getProperty(String key) {
        return getProperty(key, null);
    }
    public String getProperty(String key, String defaultValue) {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

以下(来自另一个类,仍然在我的应用程序中)应该通过我的测试:

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";
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

我不在乎调用哪个,我只想要调用两个变体中的一个.

在Mockito,我通常可以这样做:

Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
Run Code Online (Sandbox Code Playgroud)

要么:

Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
Run Code Online (Sandbox Code Playgroud)

是否有一种本地方式可以说"验证其中一个或另一个至少发生过一次"?

或者我必须做一些粗暴的事情:

try {
    Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
} catch (AssertionError e) {
    Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
}
Run Code Online (Sandbox Code Playgroud)

ach*_*ach 8

您可以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
Run Code Online (Sandbox Code Playgroud)


Jef*_*ica 5

一般来说,如果您调用verify任何类型的“getter”,您就对实现假设太多。Mockito 通常是为灵活的测试而设计的(相比之下,即使代码正确也需要更改的“脆弱”测试);您的测试应该更关心该值是否正确,而不是使用哪些方法来获取该值。更好的解决方案可能是对两个 getter进行存根以返回可预测的值,然后对相同的值使用正常的断言以确保它到达正确的位置。

when(mockFoo.getProperty("bar")).thenReturn("bar value");
when(mockFoo.getProperty("bar", anyString())).thenReturn("bar value");
// ...
assertEquals("bar value", new SystemUnderTest(mockFoo).getBarProperty());
Run Code Online (Sandbox Code Playgroud)

Mockito 的文档说明了这一点:

尽管可以验证存根调用,但通常这是多余的。假设你已经被打断了foo.bar()。如果您的代码关心foo.bar()返回什么,那么其他东西就会中断(通常甚至在verify()执行之前)。如果您的代码不关心get(0)返回什么,那么它不应该被存根。

Mockito.mockingDetails也就是说,如果这是您需要支持的模式(或具有重载和副作用的方法调用),您可以通过和获取大量信息MockingDetails.getInvocations,包括从 Mockito 1.10.0 开始的调用。您需要循环调用对象来检查多个方法。

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);
Run Code Online (Sandbox Code Playgroud)

请注意,第二种解决方案有点危险,因为它无法受益于 IDE 内置的自动重构工具,并且可能比其他一些解决方案更难阅读。(上面的内容也可能缺少对isIgnoredForVerificationmarkVerified和其他细节的调用。)但是,如果您预见到在大型代码库中经常需要这样做,那么使用 Mockito 的内置 API 可能会为您提供比其他方式更大的灵活性。