Using Groovy MetaClass to overwrite Methods

raoulsson picture raoulsson · Dec 18, 2009 · Viewed 25.3k times · Source

I have a POJO that uses a service to do something:

public class PlainOldJavaObject {

    private IService service;

    public String publicMethod(String x) {
        return doCallService(x);
    }

    public String doCallService(String x) {
        if(service == null) {
            throw new RuntimeException("Service must not be null");
        }
        return service.callX(x);
    }

    public interface IService {
        String callX(Object o);
    }
}

And I have a Groovy test case:

class GTest extends GroovyTestCase {

    def testInjectedMockIFace() {
        def pojo = new PlainOldJavaObject( service: { callX: "very groovy" } as IService )
        assert "very groovy" == pojo.publicMethod("arg")
    }

    def testMetaClass() {
        def pojo = new PlainOldJavaObject()
        pojo.metaClass.doCallService = { String s ->
            "no service"
        }
        assert "no service" == pojo.publicMethod("arg")
    }
}

The first test method, testInjectedMockIFace works as expected: The POJO is created with a dynamic implementation of IService. When callX is invoked, it simply returns "very groovy". This way, the service is mocked out.

However I don't understand why the second method, testMetaClass does not work as expected but instead throws a NullPointerException when trying to invoke callX on the service object. I thought I had overwritten the doCallService method with this line:

pojo.metaClass.doCallService = { String s ->

What am I doing wrong?

Thanks!

Answer

l15a picture l15a · Mar 8, 2010

Your syntax is a tiny bit off. The problem is that pojo is a Java Object and does not have a metaClass. To intercept calls to PlainOldJavaObject's doCallService using ExpandoMetaClass:

Just replace:

    pojo.metaClass.doCallService = { String s ->
        "no service"
    }

With:

    PlainOldJavaObject.metaClass.doCallService = { String s ->
        "no service"
    }