Monday, June 08, 2009

Mockito non-hamcrest any matcher

These days I'm using Mockito for my behavior based tests. I like Mockito's integration with Hamcrest, but I don't always like the viral matcher requirement. In particular, if I have a method that takes 3 arguments, I don't like the fact that if I use a matcher for one argument I have to use a matcher for all 3. For example, in the following verification I don't care about the callback instance, but I do care about the timeout and the async flag.

verify(channel).subscribe(any(Callback.class), eq(100), eq(true))

I was toying with some code the other day and it occurred to me that I should be able to write my own any method that achieves what I'm looking for without requiring my other arguments to be matchers.

The code below is what I've started using as an alternative.

    public static  T any(final Class clazz) {
MethodInterceptor advice = new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().equals("equals")) {
return clazz.isInstance(obj);
}
return null;
}
};

return new ProxyCreator().imposterise(advice, clazz);
}

Using my any implementation the first example code can be written like the example below.

verify(channel).subscribe(any(Callback.class), 100, true)

My implementation relies on classes from cglib and spruice; however, you could copy the necessary class from spruice very easily. Here are the referenced classes:
  • net.sf.cglib.proxy.MethodInterceptor
  • net.sf.cglib.proxy.MethodProxy
  • org.spruice.proxy.ProxyCreator
There may be limitations of this implemenation, but it's been working fine for me. Please let me know if you spot a potential improvement.

6 comments:

  1. Anonymous4:53 AM

    A very elegant solution, thanks for sharing :-)

    We had run into similar problems with the matchers.

    ReplyDelete
  2. Wow. Interesting idea! I'd like to incorporate it into Mockito somehow. For some classes you would still need to use good old any() and eq() (e.g. final classes, classes with final equals() :-D )

    ReplyDelete
  3. Anonymous9:42 AM

    Szczepan, Please feel free to add the idea to Mockito. I'm glad you like it. =)

    Cheers, Jay

    ReplyDelete
  4. Ashwyn12:36 AM

    Hey Jay , for the 'T' modifier
    i'm getting the following import
    import org.apache.poi.hssf.record.formula.functions.T;

    is this correct?

    ReplyDelete
  5. Anonymous7:05 AM

    Ashwyn, T is just a generic type. It's not something you should need to import.

    Cheers, Jay

    ReplyDelete
  6. Just use Mockito.eq(T)

    ReplyDelete

Note: Only a member of this blog may post a comment.