Testing Grails JAX-RS resources with Spock

The Missing Infrastructure

Developing with Grails is a real delight, and testing my delightful Grails applications with Spock makes the experience even better.
Some of Grails’ test cases, such as the Controllers, require a specialized setup because you don’t test them in the same manner as you would test PO[GJ]Os; you must mock requests and such.

When testing the REST resources provided by the excellent JAX-RS plugin, the situation is no different; we can test the resources only through specialized cases where a proper environment has been set up.
Thankfully, the authors of the JAX-RS plugin have already provided us with great infrastructure for integration testing with JUnit.
But alas, testing with Spock requires specialized classes because Spock uses a different inheritance hierarchy, and uses different methods for setup/tear-down and sharing data between specifications; so some work is required.

The Solution

Future Solution

The good news is that I’ve already prepared a pull request that adds this functionality, and it was merged in to the project fairly quickly (thanks Martin!).

Current Solution

So how do we get by until the next version of the plugin is released? I’ll now show you how easy it is to add a temporary Spock test infrastructure to your project.
We’ll simply clone the existing JUnit integration test base and adapt it to use the facilities provided by Spock.

1. Dependencies

First and foremost, let’s add a dependency on the latest Grails Spock plugin.
We’ll edit our project’s BuildConfig.groovy (which is located at $PROJECT_HOME/grails-app/conf/BuildConfig.groovy) and add the plugin dependency configuration:

grails.project.dependency.resolution = {
    plugins {
        test ":spock:0.7"
    }
}

Note that the grails.project.dependency.resolution and plugins configuration hierarchies might already exist in the file.

2. The Base Test Specification

Once our project has been reloaded and the dependencies have been resolved, we can create our base test specification.
Let’s create a new Groovy class named RestApiIntegrationSpec within our project’s integration test folder ($PROJECT_HOME/test/integration) and add the following content:

package org._10ne.example.interaction.test

import grails.plugin.spock.IntegrationSpec
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.grails.jaxrs.JaxrsController
import org.grails.jaxrs.itest.IntegrationTestEnvironment
import org.grails.jaxrs.web.JaxrsContext
import org.grails.jaxrs.web.JaxrsUtils
import spock.lang.Shared

import javax.servlet.http.HttpServletResponse

/**
 * @author Noam Y. Tenne
 */
class RestApiIntegrationSpec extends IntegrationSpec {

    @Shared
    IntegrationTestEnvironment testEnvironment

    GrailsApplication grailsApplication

    def controller

    @Shared
    def headers = ['Content-Type': 'application/json', 'Accept': 'application/json']

    def setupSpec() {
        testEnvironment = null
    }

    def setup() {
        grailsApplication.config.org.grails.jaxrs.dowriter.require.generic.collections = false
        grailsApplication.config.org.grails.jaxrs.doreader.disable = false
        grailsApplication.config.org.grails.jaxrs.dowriter.disable = false

        if (!testEnvironment) {
            testEnvironment = new IntegrationTestEnvironment(contextLocations, jaxrsImplementation, jaxrsClasses, autoDetectJaxrsClasses)
        }

        controller = new JaxrsController()
        controller.jaxrsContext = testEnvironment.jaxrsContext
    }

    void setRequestUrl(String url) {
        JaxrsUtils.setRequestUriAttribute(controller.request, url)
    }

    void setRequestMethod(String method) {
        controller.request.method = method
    }

    void setRequestContent(byte[] content) {
        controller.request.content = content
    }

    void addRequestHeader(String key, Object value) {
        controller.request.addHeader(key, value)
    }

    void resetResponse() {
        controller.response.committed = false
        controller.response.reset()
    }

    HttpServletResponse getResponse() {
        controller.response
    }

    HttpServletResponse sendRequest(String url, String method, byte[] content = ''.bytes) {
        sendRequest(url, method, [:], content)
    }

    HttpServletResponse sendRequest(String url, String method, Map<String, Object> headers, byte[] content = ''.bytes) {
        resetResponse()

        requestUrl = url
        requestMethod = method
        requestContent = content

        headers.each { entry ->
            addRequestHeader(entry.key, entry.value)
        }

        controller.handle()
        controller.response
    }

    /**
     * Implementors can define additional Spring application context locations.
     */
    String getContextLocations() {
        ''
    }

    /**
     * Returns the JAX-RS implementation to use. Default is 'jersey'.
     */
    String getJaxrsImplementation() {
        JaxrsContext.JAXRS_PROVIDER_NAME_JERSEY
    }

    /**
     * Returns the list of JAX-RS classes for testing. Auto-detected classes
     * will be added to this list later.
     */
    List getJaxrsClasses() {
        []
    }

    /**
     * Determines whether JAX-RS resources or providers are auto-detected in
     * <code>grails-app/resources</code> or <code>grails-app/providers</code>.
     *
     * @return true is JAX-RS classes should be auto-detected.
     */
    boolean isAutoDetectJaxrsClasses() {
        true
    }
}

You’ll notice that the changes we made to the original class have been fairly subtle:

  1. The file ends with “Spec” instead of “Test” – This way we adhere to Spock’s naming conventions which are depended upon the by the different build and test report frameworks.
  2. The class extends grails.plugin.spock.IntegrationSpec – This will provide us with the different Grails facilities required for the tests.
  3. Variables that are shared between specifications are annotated with @Shared instead of being made static.
  4. Procedures that must be run once before the whole test’s executed are called from the setupSpec method.
  5. Procedures that must be run once before the every specification are called from the setup method.
3. The Tests

Finally, to write our tests, all we must do is extend our RestApiIntegrationSpec class!

Have fun testing!

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s