Provider Contract Testing with REST Docs and Stubs in Nexus or Artifactory

In this flow, we do not use a Spring Cloud Contract plugin to generate tests and stubs. We write Spring RESTDocs, and, from them, we automatically generate stubs. Finally, we set up our builds to package the stubs and upload them to the stub storage site — in our case, Nexus or Artifactory.

Producer Flow

As a producer, we:

  1. Write RESTDocs tests of our API.

  2. Add Spring Cloud Contract Stub Runner starter to our build (spring-cloud-starter-contract-stub-runner), as follows:

    • Maven

    • Gradle

    <dependencies>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    		<scope>test</scope>
    	</dependency>
    </dependencies>
    
    <dependencyManagement>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-dependencies</artifactId>
    			<version>${spring-cloud.version}</version>
    			<type>pom</type>
    			<scope>import</scope>
    		</dependency>
    	</dependencies>
    </dependencyManagement>
    dependencies {
    	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner'
    }
    
    dependencyManagement {
    	imports {
    		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    	}
    }
  3. We set up the build tool to package our stubs, as follows:

    • Maven

    • Gradle

    <!-- pom.xml -->
    <plugins>
    	<plugin>
    		<groupId>org.apache.maven.plugins</groupId>
    		<artifactId>maven-assembly-plugin</artifactId>
    		<executions>
    			<execution>
    				<id>stub</id>
    				<phase>prepare-package</phase>
    				<goals>
    					<goal>single</goal>
    				</goals>
    				<inherited>false</inherited>
    				<configuration>
    					<attach>true</attach>
    					<descriptors>
    						${basedir}/src/assembly/stub.xml
    					</descriptors>
    				</configuration>
    			</execution>
    		</executions>
    	</plugin>
    </plugins>
    
    <!-- src/assembly/stub.xml -->
    <assembly
    	xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    	<id>stubs</id>
    	<formats>
    		<format>jar</format>
    	</formats>
    	<includeBaseDirectory>false</includeBaseDirectory>
    	<fileSets>
    		<fileSet>
    			<directory>${project.build.directory}/generated-snippets/stubs</directory>
    			<outputDirectory>META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings</outputDirectory>
    			<includes>
    				<include>**/*</include>
    			</includes>
    		</fileSet>
    	</fileSets>
    </assembly>
    task stubsJar(type: Jar) {
    	classifier = "stubs"
    	into("META-INF/${project.group}/${project.name}/${project.version}/mappings") {
    		include('**/*.*')
    		from("${project.buildDir}/generated-snippets/stubs")
    	}
    }
    // we need the tests to pass to build the stub jar
    stubsJar.dependsOn(test)
    bootJar.dependsOn(stubsJar)

Now, when we run the tests, stubs are automatically published and packaged.

The following UML diagram shows the producer flow:

"API Producer"->"API Producer": write RESTDocs tests
"API Producer"->"API Producer": add the stub runner\nstarter dependency
"API Producer"->"API Producer": setup the build tool to package\nthe generated stubs
"API Producer"->"API Producer\nbuild": run the build
"API Producer\nbuild"->"RESTDocs": generate HTTP snippets
"RESTDocs"->"Spring Cloud\nContract": generate HTTP stubs
"RESTDocs"->"Spring Cloud\nContract": (optional) generate\ncontract DSLs
"Spring Cloud\nContract"->"RESTDocs": files generated
"RESTDocs"->"API Producer\nbuild": snippets generated
"API Producer\nbuild"->"API Producer\nbuild": tests passed
"API Producer\nbuild"->"API Producer\nbuild": generate stubs jar
"API Producer\nbuild"->"Stub Storage": upload JAR with the application
"API Producer\nbuild"->"Stub Storage": upload JAR with the stubs
"Stub Storage"->"API Producer\nbuild": JARs uploaded
"API Producer\nbuild"->"API Producer": build successful

Consumer Flow

Since the consumer flow is not affected by the tool used to generate the stubs, you can read Developing Your First Spring Cloud Contract-based Application to see the flow for consumer side of the provider contract testing with stubs in Nexus or Artifactory.