JMock Actions API

I use JMock as my preferred mocking library. Most of the time the expectations I write are pretty simple, indicating that a mock should return this value, or should throw this exception. For example:

mockery.checking(new Expectations() {{
 allowing(mockConfiguration).getString(
     "nakedobjects.persistor.adapter-factory",
     PojoAdapterFactory.class.getName());
 will(returnValue(PojoAdapterFactory.class.getName()));
}});

However, I recently needed wrote a little API whereby Injectable components will inject themselves into any objects that declare itself to be aware of that component. For example, we have a SpecificationLoader:

public class SpecificationLoaderImpl
    extends SpecificationLoader
    implements Injectable {
  public void injectInto(Object candidate) {
    if (SpecificationLoaderAware.class.isAssignableFrom(
        candidate.getClass())) {
      SpecificationLoaderAware cast = SpecificationLoaderAware.class.cast(candidate);
      cast.setSpecificationLoader(this);
    }
  }
}

Elsewhere I then have code such as:

specificationLoader.injectInto(someObject);

and don’t have to worry whether someObject is aware of SpecificationLoader or not.

But, what if I have a mock SpecificationLoader? How do I tell it to behave correctly if we call its injectInto() method? In fact, how can I do this for any Injectable component? This is what I came up with:

public final class InjectIntoJMockAction
  implements Action {
  public void describeTo(Description description) {
    description.appendText("inject self");
  }
  // x.injectInto(y) ---> y.setXxx(x)
  public Object invoke(Invocation invocation) throws Throwable {
    Object injectable = invocation.getInvokedObject();
    Object toInjectInto = invocation.getParameter(0);
    Method[] methods = toInjectInto.getClass().getMethods();
    for(Method method: methods) {
      if (!method.getName().startsWith("set")) {
        continue;
      }
      if (method.getParameterTypes().length != 1) {
        continue;
      }
      Class methodParameterType =
        method.getParameterTypes()[0];
      if (methodParameterType.isAssignableFrom(
            injectable.getClass())) {
        method.invoke(toInjectInto, injectable);
        break;
      }
    }
    return null;
  }
  public static InjectIntoJMockAction injectSelf() {
    return new InjectIntoJMockAction();
  }
}

This code searches for a setter that takes a single argument the type of the mock in question, and if found then invokes it. And this is what the expectation on the mock looks like:

import static yada.yada.InjectIntoJMockAction.injectInto;
...
context.checking(new Expectations() {{
 allowing(any(Injectable.class)).method("injectInto").with(any(Object.class));
 will(injectSelf());
}}

Seems to work well for me, and a nice example I think of one of the lesser known corners of the power of JMock. The only thing that would be nice is if the:

allowing(any(Injectable.class))

returned an reference of type Injectable. As it is, the use of method(“injectInto”) is a little fragile if I were to refactor the name.

About these ads

Posted on November 20, 2008, in Uncategorized and tagged . Bookmark the permalink. 4 Comments.

  1. That’s a good idea. We’ll look into whether that’s possible.

  2. I’ve created a JIRA issue for this:http://jira.codehaus.org/browse/JMOCK-207

  3. SpecificationLoaderAware.class.isAssignableFrom(candidate.getClass())

    should be written as

    candidate instanceof SpecificationLoaderAware

    methodParameterType.isAssignableFrom(injectable.getClass())

    should be written as

    methodParameterType.isInstance(injectable)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 263 other followers