Reputation: 6069
Suppose I have a class with two methods where I don't care which is called...
public class Foo {
public String getProperty(String key) {
return getProperty(key, null);
}
public String getProperty(String key, String defaultValue) {
//...
}
}
Both the below (from another class, still in my application) should pass my test:
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";
}
// ...
}
I don't care which was called, I just want one of the two variants to be called.
In Mockito, I can generally do things like this:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
Or:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
Is there a native way to say "verify either one or the other occurred at least once"?
Or do I have to do something as crude as:
try {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
} catch (AssertionError e) {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
}
Upvotes: 22
Views: 9750
Reputation: 95634
Generally, if you're calling verify
on a "getter" of any sort, you're assuming too much about the implementation. Mockito is generally designed for flexible tests (compared to "brittle" test that need to change even if the code is correct); your test should care more about whether the value is correct as opposed to which methods were used to get that value. A better solution might be to stub both getters to return a predictable value, and then use a normal assertion against the same value to ensure it plumbs through to the correct place.
when(mockFoo.getProperty("bar")).thenReturn("bar value");
when(mockFoo.getProperty("bar", anyString())).thenReturn("bar value");
// ...
assertEquals("bar value", new SystemUnderTest(mockFoo).getBarProperty());
Mockito's documentation spells this out:
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.
That said, if this is a pattern you're required to support (or a method call with both overloads and side-effects) you can get a lot of information via Mockito.mockingDetails
and MockingDetails.getInvocations
, including invocations as of Mockito 1.10.0. You would need to loop through the Invocation objects to check against multiple methods.
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);
Note that this second solution is a little dangerous, as it does not benefit from automatic refactoring tools built into IDEs, and may be harder to read than some other solutions. (The above may also be missing calls to isIgnoredForVerification
, markVerified
, and other niceties.) However, if you foresee needing this frequently across a large codebase, then using Mockito's built-in APIs may afford you much more flexibility than you would have otherwise.
Upvotes: 6
Reputation: 6234
You could use atLeast(0)
in combination with 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
Upvotes: 15
Reputation: 10618
In your particular case, getProperty(String)
calls getProperty(String, String)
internally.
public String getProperty(String key) {
/*
* getProperty(String, String) is called anyway.
* Why not simply verify the occurrence of that?
*/
return getProperty(key, null);
}
Simply verifying the second method would be equivalent to verifying the occurrence of either one or the other at least once.
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
Upvotes: 0