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
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

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Why and How To Integrate Elastic APM in Apache JMeter
  • How To Get Cell Data From an Excel Spreadsheet Using APIs in Java
  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • 8 Strategies To Accelerate Web Portal Development

Trending

  • Automatic Code Transformation With OpenRewrite
  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • Vibe Coding With GitHub Copilot: Optimizing API Performance in Fintech Microservices
  1. DZone
  2. Coding
  3. Java
  4. Groovy's RESTClient with Spock Extensions

Groovy's RESTClient with Spock Extensions

Groovy has an extension to its HTTPBuilder class called RESTClient which makes it fairly easy to test a RESTful web service.

By 
Geraint Jones user avatar
Geraint Jones
·
Dec. 05, 12 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
31.9K Views

Join the DZone community and get the full member experience.

Join For Free

Groovy has an extension to its HTTPBuilder class called RESTClient which makes it fairly easy to test a RESTful web service. This post will describe how to use the RESTClient, how to inject it into a Groovy Spock test class via custom annotations, how to swap the injected client between a real one and a mocked one using Mockito, and how to test it.

The RESTClient class provides methods to perform the HTTP's GET, POST, PUT and DELETE methods. The RESTClient instance can be constructed with a url string and optionally a content type. The HTTP methods can then be called with a named parameter list. For example, the below code will create a RESTClient instance and call the get method passing in the path and a path parameter:

def client = new RESTClient("http://localhost:8080/testWebService/")
def resp = client.get(path : "server/status/ServerOne")

The response returned is an instance of HttpResponseDecorator. This wraps the HttpResponse to allow for easy access to the data which is parsed depending upon the content type. The below class shows it's use within a Spock framework test and how the parsed data can be extracted from the response object. The returned XML is:

<serverstatus>
  <servername>ServerOne</servername>
  <isrunning>true</isrunning>
</serverstatus>

then

<serverstatus>
  <servername>ServerTwo</servername>
  <isrunning>false</isrunning>
</serverstatus>
import groovy.util.slurpersupport.GPathResult
import groovyx.net.http.RESTClient
import spock.lang.*
import groovyx.net.http.ContentType

class InjectedRestServiceTest extends Specification {

 @RESTWebClient
 def client

 def "Test Server Statuses"() {

  when: "retrieve server status"
  def resp1 = client.get(path : "server/status/ServerOne")
  def resp2 = client.get(path : "server/status/ServerTwo")

  then: "test server one response"
  assert resp1.data.serverName.text() == "ServerOne"
  assert resp1.data.isRunning.text() == "true"

  then: "test server two response"
  assert resp2.data.serverName.text() == "ServerTwo"
  assert resp2.data.isRunning.text() == "false"

 }

}

The test asserts are purely there in this example to prove that the response from the real web service are the same as from the mocked web service. In reality, you would be testing some other functionality of which calling a RESTful web service is part of the flow. You wouldn't really assert the response from a mocked RESTClient although you may want to verify that it has been called n times.

The InjectedRestServiceTest class has the RESTClient injected using the custom made annotation @RESTWebClient. There are three classes involved which enable this functionality. First the interface, which is marked as only visible at field level, contains an @ExtensionAnnotation specifying the class which basically tells Spock what to do when it encounters the @RESTClient annotation:

import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import org.spockframework.runtime.extension.ExtensionAnnotation

@Retention(RetentionPolicy.RUNTIME)
@Target([ ElementType.FIELD ])
@ExtensionAnnotation(RESTWebClientAnnotationDrivenExtension)

public @interface RESTWebClient{}

The RESTWebClientAnnotationDrivenExtension class in this example only needs to override the visitFieldAnnotation method of the AbstractAnnotationDrivenExtension class because of the annotation's target element type:

import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.extension.AbstractMethodInterceptor
import org.spockframework.runtime.extension.IMethodInvocation
import org.spockframework.runtime.model.FieldInfo
import org.spockframework.runtime.model.SpecInfo

class RESTWebClientAnnotationDrivenExtension extends
 AbstractAnnotationDrivenExtension<RESTWebClient> {

 private static final String url =
  System.getProperties().getProperty("url")
 private static final boolean useMock =
  Boolean.parseBoolean(System.getProperties().getProperty("useMock"))

 @Override
 void visitFieldAnnotation(RESTWebClient annotation, FieldInfo field) {
  def methodInterceptor = new RESTWebClientMethodInterceptor(field, url, useMock)
  methodInterceptor.install(field.parent)
 }

}

The above extension tells Spock that when it encounters @RESTClient, it must create an interceptor with the arguments denoting the field to which the RESTClient instance is to be set, the url of the web service and a boolean to specify if the mock is to be used or the real web service should be used. (The meaning of the two values could be combined to just have a url and if set then the real web service must be used.)

The interceptor will create either a mock RESTClient or a real one. It will do this when the client field needs to be set in the InjectedRestServiceTest class. If the real RESTClient is required then an instance using the url will be created and set using the fieldInfo.writeValue method. If a mock is required then a Mockito mock RESTClient is created and the expected behaviour mocked using instances of the class HttpResponseDecorator (although these could be mocks too.) Again , this is set using the fieldInfo.writeValue method:

import groovy.mock.interceptor.MockFor
import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.RESTClient
import org.spockframework.runtime.extension.AbstractMethodInterceptor;
import org.spockframework.runtime.extension.IMethodInvocation;
import org.spockframework.runtime.model.FieldInfo;
import org.spockframework.runtime.model.SpecInfo;

import static org.mockito.Mockito.*
import static org.mockito.Matchers.*

class RESTWebClientMethodInterceptor extends AbstractMethodInterceptor {

 private final FieldInfo fieldInfo
 private final String url
 private final boolean useMock

 RESTWebClientMethodInterceptor(FieldInfo fieldInfo, String url, boolean useMock) {
  this.fieldInfo = fieldInfo
  this.url = url
  this.useMock = useMock
 }

 @Override
 void interceptSetupMethod(IMethodInvocation methodInvocation) {
  setupRESTClient(methodInvocation.target)
  methodInvocation.proceed()
 }

 @Override
 void install(SpecInfo specInfo) {
  specInfo.setupMethod.addInterceptor this
 }

 private void setupRESTClient(target) {

  if (useMock) {

   def xmlServerOne =
     """
      <serverStatus>
        <serverName>ServerOne</serverName>
        <isRunning>true</isRunning>
      </serverStatus>
      """

   def xmlServerTwo =
     """
      <serverStatus>
        <serverName>ServerTwo</serverName>
        <isRunning>false</isRunning>
      </serverStatus>
      """

   def httpResponseDecoratorServerOne = new HttpResponseDecorator(
     null, new XmlParser().parseText(xmlServerOne))
   def httpResponseDecoratorServerTwo = new HttpResponseDecorator(
     null, new XmlParser().parseText(xmlServerTwo))
   def mapServerOne = [path:"server/status/ServerOne"]
   def mapServerTwo = [path:"server/status/ServerTwo"]

   RESTClient mockRESTClient = org.mockito.Mockito.mock(RESTClient)
   when(mockRESTClient.get(mapServerOne)).thenReturn(httpResponseDecoratorServerOne);
   when(mockRESTClient.get(mapServerTwo)).thenReturn(httpResponseDecoratorServerTwo);

   fieldInfo.writeValue(target, mockRESTClient)
  }
  else {
   fieldInfo.writeValue(target, new RESTClient(url))
  }
 }

}

Whilst there are different ways to mock and test RESTful web services, the above classes do show how easily RESTful web services can be called and their responses parsed, how annotations can be created and how objects can be mocked. Similar examples could be constructed for different aspects of a Spock test eg features, fixtures etc..

More can be found on extensions at http://code.google.com/p/spock/wiki/SpockBasics#Extensions and on RESTClient at http://groovy.codehaus.org/modules/http-builder/doc/rest.html






Spock (testing framework) Web Service Groovy (programming language)

Published at DZone with permission of Geraint Jones, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Why and How To Integrate Elastic APM in Apache JMeter
  • How To Get Cell Data From an Excel Spreadsheet Using APIs in Java
  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • 8 Strategies To Accelerate Web Portal Development

Partner Resources

×

Comments

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: