Spring Cloud Contract Stub Runner

One of the issues that you might encounter while using Spring Cloud Contract Verifier is passing the generated WireMock JSON stubs from the server side to the client side (or to various clients). The same takes place in terms of client-side generation for messaging.

Copying the JSON files and setting the client side for messaging manually is out of the question. That is why we introduced Spring Cloud Contract Stub Runner. It can automatically download and run the stubs for you.

Snapshot versions

Add the additional snapshot repository to your build.gradle file to use snapshot versions, which are automatically uploaded after every successful build:

Maven
Gradle

Publishing Stubs as JARs

The easiest approach would be to centralize the way stubs are kept. For example, you can keep them as jars in a Maven repository.

For both Maven and Gradle, the setup comes ready to work. However, you can customize it if you want to.
Maven
<!-- First disable the default jar setup in the properties section -->

<!-- Next add the assembly plugin to your build -->

<!-- Finally setup your assembly. Below you can find the contents of src/main/assembly/stub.xml -->
Gradle

Common

This section briefly describes common properties, including:

Common Properties for JUnit and Spring

You can set repetitive properties by using system properties or Spring configuration properties. Here are their names with their default values:

Property name Default value Description

stubrunner.minPort

10000

Minimum value of a port for a started WireMock with stubs.

stubrunner.maxPort

15000

Maximum value of a port for a started WireMock with stubs.

stubrunner.repositoryRoot

Maven repo URL. If blank, then call the local maven repo.

stubrunner.classifier

stubs

Default classifier for the stub artifacts.

stubrunner.stubsMode

CLASSPATH

The way you want to fetch and register the stubs

stubrunner.ids

Array of Ivy notation stubs to download.

stubrunner.username

Optional username to access the tool that stores the JARs with stubs.

stubrunner.password

Optional password to access the tool that stores the JARs with stubs.

stubrunner.stubsPerConsumer

false

Set to true if you want to use different stubs for each consumer instead of registering all stubs for every consumer.

stubrunner.consumerName

If you want to use a stub for each consumer and want to override the consumer name just change this value.

Stub Runner Stubs IDs

You can provide the stubs to download via the stubrunner.ids system property. They follow this pattern:

groupId:artifactId:version:classifier:port

Note that version, classifier and port are optional.

  • If you do not provide the port, a random one will be picked.

  • If you do not provide the classifier, the default is used. (Note that you can pass an empty classifier this way: groupId:artifactId:version:).

  • If you do not provide the version, then the + will be passed and the latest one is downloaded.

port means the port of the WireMock server.

Starting with version 1.0.4, you can provide a range of versions that you would like the Stub Runner to take into consideration. You can read more about the Aether versioning ranges here.

Stub Runner Docker

We’re publishing a spring-cloud/spring-cloud-contract-stub-runner Docker image that will start the standalone version of Stub Runner.

If you want to learn more about the basics of Maven, artifact ids, group ids, classifiers and Artifact Managers, just click here [docker-project].

How to use it

Just execute the docker image. You can pass any of the Common Properties for JUnit and Spring as environment variables. The convention is that all the letters should be upper case. The camel case notation should and the dot (.) should be separated via underscore (_). E.g. the stubrunner.repositoryRoot property should be represented as a STUBRUNNER_REPOSITORY_ROOT environment variable.

Example of client side usage in a non JVM project

We’d like to use the stubs created in this [docker-server-side] step. Let’s assume that we want to run the stubs on port 9876. The NodeJS code is available here:

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

Let’s run the Stub Runner Boot application with the stubs.

# Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run  --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"

What’s happening is that

  • a standalone Stub Runner application got started

  • it downloaded the stub with coordinates com.example:bookstore:0.0.1.RELEASE:stubs on port 9876

  • it got downloaded from Artifactory running at http://192.168.0.100:8081/artifactory/libs-release-local

  • after a while Stub Runner will be running on port 8083

  • and the stubs will be running at port 9876

On the server side we built a stateful stub. Let’s use curl to assert that the stubs are setup properly.

# let's execute the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON
If you want use the stubs that you have built locally, on your host, then you should pass the environment variable -e STUBRUNNER_STUBS_MODE=LOCAL and mount the volume of your local m2 -v "${HOME}/.m2/:/root/.m2:ro"