Как я могу проверить, что один из двух методов был вызван с помощью 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());