Groovy: Creating an Interface Stub and Intercepting All Calls to It
Join the DZone community and get the full member experience.
Join For FreeIt’s sometimes useful for unit testing to be able to create a simple no-op stub of an interface the class under test depends upon and to intercept all calls to the stub, for example to remember all the calls and parameters so that you can later verify that they’ve been invoked as expected. Often you’d use something like Mockito and its verify method but if you’re writing unit tests in Groovy as I recommend then there is a simpler and in a way a more powerful alternative.
Solution 1 – When Calling the Stub From a Groovy Object
@Before public void setUp() { this.collectedCalls = [] // The following works when a method on the listener is called from Groovy but not from Java: this.listener = {} as PageNodeListener listener.metaClass.invokeMethod = { name, args -> collectedCalls << new Call(method: name, args: args) // Call is a simple structure class } } @Test public void listener_stub_should_report_all_calls_to_it() throws Exception { listener.fileEntered("fileName.dummy") assert collectedCalls.find { it.equals(new Call(method: "fileEntered", args: ["fileName.dummy"]))} }
Notes:
- 5: {} as PageNodeListener uses Groovy’s closure coercion – basically it creates an implementation of the interface which uses the (empty) closure whenever any method is called (we could capture its arguments but not the method name via {Object[] args -> /*...*/} as PageNodeListener)
- 6-7: We then specify an interceptor method that should be invoked whenever any method is called on the instance
- Groovy makes it very easy to create beans like Call with equals, toString etc.
Beware that using a coerced closure as an interface implementation works only for methods that are either void or can return null (which is the return value of an empty closure, when invoked). Other methods will throw an NPE:
def list = {} as java.util.List list.clear() // OK, void list.get(0) // OK, returns null list.isEmpty() // NullPointerException at $Proxy4.isEmpty
Solution 2 – When Calling the Stub From a Java Object
The solution 1 is small and elegant but for me it hasn’t worked when the stub was invoked by a Java object (a clarification of why that happened would be welcome). Fortunately Christoph Metzendorf proposed a solution at StackOverflow (adjusted):
@Before public void setUp() { this.collectedCalls = [] PageNodeListener listener = createListenerLoggingCallsInto(collectedCalls) this.parser = new MyFaces21ValidatingFaceletsParser(TEST_WEBROOT, listener) } def createListenerLoggingCallsInto(collectedCalls) { def map = [:] PageNodeListener.class.methods.each() { method -> map."$method.name" = { Object[] args -> collectedCalls << new Call(method.name, args) } } return map.asType(PageNodeListener.class) } @Test public void should_notify_about_file_entered() throws Exception { parser.validateExpressionsInView(toUrl("empty.xhtml"), "/empty.xhtml") assert collectedCalls.find { it.equals(new Call(method: "fileEntered", args: ["/empty.xhtml"]))} }
Notes:
- 12-13: We create a map containing {method name} -> {closure} for each of the interface’s method
- 17: The map is then coerced to the interface (the same as someMap as PageNodeListener). Notice that if it didn’t contain an entry for a method then it would throw a NullPointerException if the method was invoked on the stub.
Notice that this version is more flexible than the Groovy-only one because we have full access to java.lang.reflect.Method when we create the map and thus can adjust the return value of the method closure w.r.t. what is expected. Thus it’s possible to stub any interface, even if it has methods returning primitive types.
Conclusion
This is a nice and simple way to stub an interface with methods that are void or return non-primitive values and collect all calls to the stub for a verification. If your requirements differ then you might be better of with a different type of mocking in Groovy or with a true mocking library.
Additional Information
Groovy version: 1.8.2, Java: 6.0.
See the full source code in the JSF EL Validator’s NotifyingCompilationManagerTest.groovy.
From http://theholyjava.wordpress.com/2011/11/02/groovy-creating-interface-stub-and-intercepting-all-calls-to-it/
Opinions expressed by DZone contributors are their own.
Comments