Adam Burley
Adam Burley

Reputation: 6069

How can I verify that one of two methods was called using Mockito?

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

Answers (3)

Jeff Bowman
Jeff Bowman

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 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.

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

ach
ach

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

John Bupit
John Bupit

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

Related Questions