Как я могу проверить, что один из двух методов был вызван с помощью Mockito?

Предположим, что у меня есть класс с двумя методами, где мне все равно, что называется...

public class Foo {
    public String getProperty(String key) {
        return getProperty(key, null);
    }
    public String getProperty(String key, String defaultValue) {
        //...
    }
}

Оба ниже (из другого класса, все еще в моем приложении) должны пройти мой тест:

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());

Есть ли собственный способ сказать: "Проверить один или другой, по крайней мере, один раз"?

Или мне нужно сделать что-то столь же грубое, как:

try {
    Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
} catch (AssertionError e) {
    Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
}

Ответ 1

Вы можете использовать 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

Ответ 2

Как правило, если вы вызываете verify в "getter" любого типа, вы слишком много думаете о реализации. Mockito обычно предназначен для гибких тестов (по сравнению с "хрупким" тестом, который необходимо изменить, даже если код правильный); ваш тест должен больше заботиться о правильности значения, а не о том, какие методы использовались для получения этого значения. Лучшее решение может состоять в том, чтобы заглушить оба геттера, чтобы вернуть предсказуемое значение, а затем использовать нормальное утверждение против того же значения, чтобы обеспечить его прохождение в нужное место.

when(mockFoo.getProperty("bar")).thenReturn("bar value");
when(mockFoo.getProperty("bar", anyString())).thenReturn("bar value");
// ...
assertEquals("bar value", new SystemUnderTest(mockFoo).getBarProperty());

Документация Mockito гласит следующее:

Хотя можно проверить зашитый вызов, обычно он просто лишний. Скажем, вы зарезали foo.bar(). Если ваш код заботится о том, что foo.bar() возвращает, то что-то еще ломается (часто до выполнения verify()). Если вашему коду не волнует, что возвращает get(0), его не следует обрезать.

Тем не менее, если это шаблон, который вам требуется для поддержки (или вызов метода с перегрузками и побочными эффектами), вы можете получить много информации через Mockito.mockingDetails и MockingDetails.getInvocations, включая вызовы от Mockito 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);

Обратите внимание, что это второе решение немного опасно, так как оно не использует инструменты автоматической рефакторинга, встроенные в IDE, и их может быть труднее читать, чем некоторые другие решения. (Вышеупомянутые могут также отсутствовать вызовы isIgnoredForVerification, markVerified и другие тонкости.) Однако, если вы предусмотрите необходимость этого часто в большой базе кода, то использование встроенных API-интерфейсов Mockito может позволить вам гораздо большую гибкость, чем вы в противном случае.

Ответ 3

В вашем конкретном случае 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());