7

I'm learning Mockito at the moment and one of the things I'm doing to consolidate my learning is converting an old JUnit test from using a hand rolled mock class to one which uses Mockito mocks instead. However, I've run into a situation I'm not sure how to handle.

Specifically, my unit under test constructs a String which gets passed to the mocked object as a parameter in a method call on it. I'd like to test that the String is constructed correctly. The challenge is that part of the String is a hash key which is generated internally and changes on every invocation. One solution that would work would be to get the hash generation under my control and inject a dummy generator for test execution. However, this is a fair bit of work.

My old hand rolled mock class would store the arguments passed to it which I could query in my test. This allowed me to query the start and end of the String via the following:

assertTrue(mockFtpClient.getFilePathAndName().startsWith("/data/inbound/XJSLGG."));
assertTrue(mockFtpClient.getFilePathAndName().endsWith(".pdf"));

This was a sufficent enough test for my taste. So my question is, is it possible using Mockito to query or get a hold of the arguments passed to a method so that i can perform something similiar to the above?

UPDATE 24/06/2011: At this point I have excepted Gnon's answer. However, I have since discovered something which works better for me. Namely ArgumentCaptor. Here's how it works:

ArgumentCaptor<String> fileNameArgument = ArgumentCaptor.forClass(String.class);
verify(mockFtpClient).putFileOnServer(fileNameArgument.capture());
assertTrue(fileNameArgument.getValue().startsWith(START_FILE_NAME) &&
           fileNameArgument.getValue().endsWith(END_FILE_NAME));

The javadoc for Mockito state that ArgumentCaptor is generally a better choice when you have a one-off specific argument matching requirement, as I do here.

Chris Knight
  • 24,333
  • 24
  • 88
  • 134

2 Answers2

3

Basically you need to use argThat() in Mockito, that lets you treat a Hamcrest Matcher as a verification argument. Here is the code you use to make custom assertions about a passed-in argument:

@Test
public void testname() throws Exception {
    HashReceiver receiver = mock(HashReceiver.class);
    receiver.set("hash");
    verify(receiver).set(argThat(new HashMatcher()));
}

class HashMatcher extends BaseMatcher<String> {
    @Override
    public boolean matches(Object item) {
        String hash = (String) item;
        if (hash.startsWith("/data/inbound/XJSLGG.") && hash.endsWith(".pdf"))
            return true;
        return false;
    }
}

// Mocked
class HashReceiver {
    public void set(String hash) {
    }
}

You may be able to use a generic matcher instead, or a combination of generic matchers.

jwaddell
  • 1,892
  • 1
  • 23
  • 32
Garrett Hall
  • 29,524
  • 10
  • 61
  • 76
  • Thanks Gnon, I think this solution works really well for what I need. Shame its so much extra code, but then this isn't a common scenario. – Chris Knight Jun 23 '11 at 22:25
  • This is where you'll find the argThat() method: http://docs.mockito.googlecode.com/hg/latest/org/mockito/Matchers.html#argThat(org.hamcrest.Matcher). Your import should be `import static org.mockito.Matchers.argThat;` – Tom Saleeba Sep 15 '14 at 07:43
1

Have a look at the accepted answer to this question mockito-how-to-make-a-method-return-an-argument-that-was-passed-to-it it will show you how to get a hold of the arguments passed to your mock method invocation.

Community
  • 1
  • 1
digitaljoel
  • 26,265
  • 15
  • 89
  • 115
  • Not bad, but using the Answer object to get a hold of the input parameter seems a bit backwards to me and I'm not entirely sure it would work with my void method signature. Thanks though! I didn't know about `Answer` before. – Chris Knight Jun 23 '11 at 22:24