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!
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"
}
If your POJO really is a Java class, and not a Groovy class, then that is your problem. Java classes don't invoke methods via the metaClass. e.g., in Groovy:
pojo.publicMethod('arg')
is equivalent to this Java:
pojo.getMetaClass().invokeMethod('publicMethod','arg');
invokeMethod
sends the call through the metaClass. But this method:
public String publicMethod(String x) {
return doCallService(x);
}
is a Java method. It doesn't use invokeMethod
to call doCallService
. To get your code to work, PlainOldJavaObject
needs to be a Groovy class so that all calls will go through the metaClass. Normal Java code doesn't use metaClasses.
In short: even Groovy can't override Java method calls, it can only override calls from Groovy or that otherwise dispatch through invokeMethod.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With