Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Ratpacked: Using Spring Cloud Contract

DZone's Guide to

Ratpacked: Using Spring Cloud Contract

Learn how to create contracts for Spring Cloud using a Groovy DSL. That way, your Ratpack apps can have responses ready for predetermined requests.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Spring Cloud Contract is a project that allows you to write a contract for a service using a Groovy DSL. In the contract, we describe the expected requests and responses for the service. From this contract, a stub is generated that can be used by a client application to test the code that invokes the service. Spring Cloud Contract also generates tests based on the contract for the service implementation. Let's see how we can use the generated tests for the service implementation for a Ratpack application.

Spring Cloud Contract comes with a Gradle plugin. This plugin adds the task generateContractTests, which creates tests based on the contract we write. There are also tasks to create the stub for a client application, but here we focus on the server implementation. In the following Gradle build file for our Ratpack application, we use the Spring Cloud Contract Gradle plugin. We configure the plugin to use Spock as the framework for the generated tests.

buildscript {
    ext {
        verifierVersion = '1.0.3.RELEASE'
    }
    repositories {
        jcenter()
    }
    dependencies {
        // We add the Spring Cloud Contract plugin to our build.
        classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifierVersion}"
    }
}
 
plugins {
    id 'io.ratpack.ratpack-java' version '1.4.5'
    id 'com.github.johnrengelman.shadow' version '1.2.4'
     
    // The Spring Cloud Contract plugin relies on
    // the Spring dependency management plugin to
    // resolve the dependency versions.
    id 'io.spring.dependency-management' version '1.0.0.RELEASE'
}
 
apply plugin: 'spring-cloud-contract'
 
repositories {
    jcenter()
}
 
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifierVersion}"
    }
}
 
dependencies {
    runtime 'org.slf4j:slf4j-simple:1.7.24'
 
    testCompile 'org.codehaus.groovy:groovy-all:2.4.9'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'
    testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
    testCompile 'commons-logging:commons-logging:1.2'
}
 
mainClassName = 'mrhaki.sample.PirateApp'
 
assemble.dependsOn shadowJar
 
/**************************************************************
 * Configure Spring Cloud Contract plugin
 *************************************************************/
contracts {
    // Of course we use Spock for the generated specifications.
    // Default is JUnit.
    targetFramework = 'Spock'
 
    // With explicit testMode real HTTP requests are sent
    // to the application from the specs.
    // Default is MockMvc for Spring applications.
    testMode = 'Explicit'
 
    // Base class with setup for starting the Ratpack
    // application for the generated specs.
    baseClassForTests = 'mrhaki.sample.BaseSpec'
 
    // Package name for generated specifications.
    basePackageForTests = 'mrhaki.sample'
}


It is time to write some contracts. We have a very basic example because we want to focus on how to use Spring Cloud Contract with Ratpack and we don't want to look into all the nice features of Spring Cloud Contract itself. In the directory src/test/resources/contracts/pirate we add a contract for the endpoint /drink:

// File: src/test/resources/contracts/pirata/drink.groovy
package contracts.pirate
 
import org.springframework.cloud.contract.spec.Contract
 
Contract.make {
    request {
        method 'GET'
        urlPath '/drink', {
            queryParameters {
                parameter 'name': $(consumer(regex('[a-zA-z]+')), producer('mrhaki'))
            }
        }
        headers {
            contentType(applicationJson())
        }
    }
    response {
        status 200
        body([response: "Hi-ho, ${value(consumer('mrhaki'), producer(regex('[a-zA-z]+')))}, ye like to drink some spiced rum!"])
        headers {
            contentType(applicationJson())
        }
    }
}


We add a second contract for an endpoint /walk:

// File: src/test/resources/contracts/pirata/walk_the_plank.groovy
package contracts.pirate
 
import org.springframework.cloud.contract.spec.Contract
 
Contract.make {
    request {
        method 'POST'
        urlPath '/walk'
        body([name: $(consumer(regex('[a-zA-z]+')), producer('mrhaki'))])
        headers {
            contentType(applicationJson())
        }
    }
    response {
        status 200
        body([response: "Ay, matey, ${value(consumer('mrhaki'), producer(regex('[a-zA-z]+')))}, walk the plank!"])
        headers {
            contentType(applicationJson())
        }
    }
}


The last step for generating the Spock specifications based on these contracts is to define a base class for the tests. Inside the base class, we use Ratpack's support for functional testing. We define our application with MainClassApplicationUnderTest and use the getAddress method to start the application and to get the port that is used for the application. The generated specifications rely on RestAssured to invoke the HTTP endpoints, so we assign the port to RestAssured:

// File: src/test/groovy/mrhaki/sample/BaseSpec.groovy
package mrhaki.sample
 
import com.jayway.restassured.RestAssured
import ratpack.test.MainClassApplicationUnderTest
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
 
abstract class BaseSpec extends Specification {
     
    @Shared
    @AutoCleanup
    def app = new MainClassApplicationUnderTest(PirateApp)
     
    def setupSpec() {
        final URI address = app.address
        RestAssured.port = address.port
    }
}


We can write the implementation for the PirateApp application and use Gradle's check tasks to let Spring Cloud Contract generate the specification and run the specifications. The specification that is generated can be found in build/generated-test-sources and looks like this:

// File: build/generated-test-sources/contracts/mrhaki/sample/PirateSpec.groovy
package mrhaki.sample
 
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
 
import static com.jayway.restassured.RestAssured.given
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson
 
class PirateSpec extends BaseSpec {
 
    def validate_drink() throws Exception {
        given:
        def request = given()
                .header("Content-Type", "application/json")
 
        when:
        def response = given().spec(request)
                              .queryParam("name", "mrhaki")
                              .get("/drink")
 
        then:
        response.statusCode == 200
        response.header('Content-Type') ==~ java.util.regex.Pattern.compile('application/json.*')
        and:
        DocumentContext parsedJson = JsonPath.parse(response.body.asString())
        assertThatJson(parsedJson).field("response").matches(
                "Hi-ho, [a-zA-z]+, ye like to drink some spiced rum!")
    }
 
    def validate_walk_the_plank() throws Exception {
        given:
        def request = given()
                .header("Content-Type", "application/json")
                .body('''{"name":"mrhaki"}''')
 
        when:
        def response = given().spec(request)
                              .post("/walk")
 
        then:
        response.statusCode == 200
        response.header('Content-Type') ==~ java.util.regex.Pattern.compile('application/json.*')
        and:
        DocumentContext parsedJson = JsonPath.parse(response.body.asString())
        assertThatJson(parsedJson).field("response").matches("Ay, matey, [a-zA-z]+, walk the plank!")
    }
 
}


If we run Gradle's check task we can see the Spring Cloud Contract plugin tasks are executed as well:

$ gradle check
:copyContracts
:generateContractTests
:compileJava
:compileGroovy NO-SOURCE
:processResources NO-SOURCE
:classes
:compileTestJava NO-SOURCE
:compileTestGroovy
:processTestResources
:testClasses
:test
:check
 
BUILD SUCCESSFUL
 
Total time: 5.749 secs


The code for the complete application is on GitHub.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,ratpack ,spring cloud contract ,groovy dsl ,tutorial

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}