DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Cypress.io — The Rising Future of Web Automation Testing
  • [CSF] LogViewer: A New Tool for Monitoring Logs
  • Consumer-Driven Contract Testing With Spring Cloud Contract
  • Why Builder Is Often an Antipattern and How to Replace it With Fluent Builder

Trending

  • Simpler Data Transfer Objects With Java Records
  • AI Agents: A New Era for Integration Professionals
  • Implementing Explainable AI in CRM Using Stream Processing
  • Useful System Table Queries in Relational Databases
  1. DZone
  2. Coding
  3. Languages
  4. Groovy: Creating an Interface Stub and Intercepting All Calls to It

Groovy: Creating an Interface Stub and Intercepting All Calls to It

By 
Jakub Holý user avatar
Jakub Holý
·
Nov. 04, 11 · Interview
Likes (0)
Comment
Save
Tweet
Share
6.3K Views

Join the DZone community and get the full member experience.

Join For Free

It’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/

Stub (distributed computing) Interface (computing) Groovy (programming language) IT

Opinions expressed by DZone contributors are their own.

Related

  • Cypress.io — The Rising Future of Web Automation Testing
  • [CSF] LogViewer: A New Tool for Monitoring Logs
  • Consumer-Driven Contract Testing With Spring Cloud Contract
  • Why Builder Is Often an Antipattern and How to Replace it With Fluent Builder

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!