Files
spring-cloud-contract/2.0.x/multi/multi__spring_cloud_contract_verifier.html
Spring Operator e460e09c0c URL Cleanup
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# HTTP URLs that Could Not Be Fixed
These URLs were unable to be fixed. Please review them to see if they can be manually resolved.

* [ ] http://aopalliance.sourceforge.net (200) with 3 occurrences could not be migrated:
   ([https](https://aopalliance.sourceforge.net) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/ (200) with 5 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_blocks.html (200) with 1 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_blocks.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_coding.html (200) with 3 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_coding.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_design.html (200) with 1 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_design.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_javadoc.html (200) with 4 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_javadoc.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_misc.html (200) with 1 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_misc.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_naming.html (200) with 1 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_naming.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_sizes.html (200) with 2 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_sizes.html) result AnnotatedConnectException).
* [ ] http://checkstyle.sourceforge.net/config_whitespace.html (200) with 2 occurrences could not be migrated:
   ([https](https://checkstyle.sourceforge.net/config_whitespace.html) result AnnotatedConnectException).
* [ ] http://codearte.io (200) with 16 occurrences could not be migrated:
   ([https](https://codearte.io) result SSLHandshakeException).
* [ ] http://docbkx-tools.sourceforge.net/docbkx-maven-plugin/ (200) with 1 occurrences could not be migrated:
   ([https](https://docbkx-tools.sourceforge.net/docbkx-maven-plugin/) result AnnotatedConnectException).
* [ ] http://erik.eae.net/archives/2007/07/27/18.54.15/ (200) with 1 occurrences could not be migrated:
   ([https](https://erik.eae.net/archives/2007/07/27/18.54.15/) result SSLHandshakeException).
* [ ] http://findbugs.sourceforge.net/ (200) with 3 occurrences could not be migrated:
   ([https](https://findbugs.sourceforge.net/) result AnnotatedConnectException).
* [ ] http://groovy-lang.org (200) with 31 occurrences could not be migrated:
   ([https](https://groovy-lang.org) result SSLProtocolException).
* [ ] http://groovy-lang.org/json.html (200) with 46 occurrences could not be migrated:
   ([https](https://groovy-lang.org/json.html) result SSLProtocolException).
* [ ] http://hsqldb.org (200) with 1 occurrences could not be migrated:
   ([https](https://hsqldb.org) result SSLHandshakeException).
* [ ] http://hsqldb.org/web/hsqlLicense.html (200) with 1 occurrences could not be migrated:
   ([https](https://hsqldb.org/web/hsqlLicense.html) result SSLHandshakeException).
* [ ] http://htmlunit.sourceforge.net (200) with 1 occurrences could not be migrated:
   ([https](https://htmlunit.sourceforge.net) result AnnotatedConnectException).
* [ ] http://javascript.nwbox.com/IEContentLoaded/ (200) with 1 occurrences could not be migrated:
   ([https](https://javascript.nwbox.com/IEContentLoaded/) result SSLHandshakeException).
* [ ] http://jtds.sourceforge.net (200) with 1 occurrences could not be migrated:
   ([https](https://jtds.sourceforge.net) result AnnotatedConnectException).
* [ ] http://nekohtml.sourceforge.net/ (200) with 1 occurrences could not be migrated:
   ([https](https://nekohtml.sourceforge.net/) result AnnotatedConnectException).
* [ ] http://objenesis.org (200) with 1 occurrences could not be migrated:
   ([https](https://objenesis.org) result SSLHandshakeException).
* [ ] http://partners.com (200) with 54 occurrences could not be migrated:
   ([https](https://partners.com) result SSLHandshakeException).
* [ ] http://planet.apache.org/ (200) with 6 occurrences could not be migrated:
   ([https](https://planet.apache.org/) result ConnectTimeoutException).
* [ ] http://rest-assured.io/ (200) with 16 occurrences could not be migrated:
   ([https](https://rest-assured.io/) result SSLHandshakeException).
* [ ] http://spockframework.org (200) with 2 occurrences could not be migrated:
   ([https](https://spockframework.org) result SSLHandshakeException).
* [ ] http://spockframework.org/ (200) with 3 occurrences could not be migrated:
   ([https](https://spockframework.org/) result SSLHandshakeException).
* [ ] http://toomuchcoding.com/blog/categories/accurest/ (200) with 16 occurrences could not be migrated:
   ([https](https://toomuchcoding.com/blog/categories/accurest/) result SSLHandshakeException).
* [ ] http://toomuchcoding.com/blog/categories/spring-cloud-contract/ (200) with 16 occurrences could not be migrated:
   ([https](https://toomuchcoding.com/blog/categories/spring-cloud-contract/) result SSLHandshakeException).
* [ ] http://wiremock.org (200) with 24 occurrences could not be migrated:
   ([https](https://wiremock.org) result SSLHandshakeException).
* [ ] http://wiremock.org/docs/stateful-behaviour/ (200) with 10 occurrences could not be migrated:
   ([https](https://wiremock.org/docs/stateful-behaviour/) result SSLHandshakeException).
* [ ] http://wiremock.org/docs/stubbing/ (200) with 7 occurrences could not be migrated:
   ([https](https://wiremock.org/docs/stubbing/) result SSLHandshakeException).
* [ ] http://wiremock.org/stateful-behaviour.html (200) with 22 occurrences could not be migrated:
   ([https](https://wiremock.org/stateful-behaviour.html) result SSLHandshakeException).
* [ ] http://wiremock.org/stubbing.html (200) with 16 occurrences could not be migrated:
   ([https](https://wiremock.org/stubbing.html) result SSLHandshakeException).
* [ ] http://www.jdom.org (200) with 1 occurrences could not be migrated:
   ([https](https://www.jdom.org) result AnnotatedConnectException).
* [ ] http://www.saxproject.org/copying.html (200) with 1 occurrences could not be migrated:
   ([https](https://www.saxproject.org/copying.html) result AnnotatedConnectException).
* [ ] http://blindsignals.com/index.php/2009/07/jquery-delay/ (301) with 1 occurrences could not be migrated:
   ([https](https://blindsignals.com/index.php/2009/07/jquery-delay/) result SSLHandshakeException).
* [ ] http://192.168.0.100:8081/artifactory/libs-release-local (403) with 14 occurrences could not be migrated:
   ([https](https://192.168.0.100:8081/artifactory/libs-release-local) result ConnectTimeoutException).
* [ ] http://takari.io/takari-lifecycle/takari-lifecycle-plugin (404) with 9 occurrences could not be migrated:
   ([https](https://takari.io/takari-lifecycle/takari-lifecycle-plugin) result ConnectTimeoutException).
* [ ] http://takari.io/takari-plugin-testing-project/takari-plugin-integration-testing (404) with 3 occurrences could not be migrated:
   ([https](https://takari.io/takari-plugin-testing-project/takari-plugin-integration-testing) result ConnectTimeoutException).
* [ ] http://takari.io/takari-plugin-testing-project/takari-plugin-testing (404) with 3 occurrences could not be migrated:
   ([https](https://takari.io/takari-plugin-testing-project/takari-plugin-testing) result ConnectTimeoutException).
* [ ] http://toomuchcoding.com/blog/2015/09/27/microservice-deployment/ (404) with 2 occurrences could not be migrated:
   ([https](https://toomuchcoding.com/blog/2015/09/27/microservice-deployment/) result SSLHandshakeException).
* [ ] http://www.seamframework.org/Weld/cdi-api (404) with 3 occurrences could not be migrated:
   ([https](https://www.seamframework.org/Weld/cdi-api) result SSLHandshakeException).
* [ ] http://xstream.codehaus.com/license.html (404) with 1 occurrences could not be migrated:
   ([https](https://xstream.codehaus.com/license.html) result ConnectTimeoutException).

# Fixed URLs

## Fixed But Review Recommended
These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended.

* [ ] http://fontawesome.io (301) with 4 occurrences migrated to:
  https://fontawesome.com?from=io ([https](https://fontawesome.io) result AnnotatedConnectException).
* [ ] http://freemarker.org/ (301) with 1 occurrences migrated to:
  https://freemarker.apache.org/ ([https](https://freemarker.org/) result ConnectTimeoutException).
* [ ] http://www.ultraq.net.nz/programming/thymeleaf-layout-dialect/ (302) with 1 occurrences migrated to:
  https://github.com/ultraq/thymeleaf-layout-dialect/ ([https](https://www.ultraq.net.nz/programming/thymeleaf-layout-dialect/) result ConnectTimeoutException).
* [ ] http://jsperf.com/getall-vs-sizzle/2 (301) with 1 occurrences migrated to:
  https://jsperf.com/getall-vs-sizzle/2 ([https](https://jsperf.com/getall-vs-sizzle/2) result ReadTimeoutException).
* [ ] http://webjars.org (301) with 5 occurrences migrated to:
  https://www.webjars.org ([https](https://webjars.org) result SSLHandshakeException).
* [ ] http://zenorocha.mit-license.org (301) with 1 occurrences migrated to:
  https://zenorocha.mit-license.org/ ([https](https://zenorocha.mit-license.org) result NullPointerException).
* [ ] http://wiki.ecmascript.org/doku.php?id=harmony:egal (ConnectTimeoutException) with 1 occurrences migrated to:
  https://wiki.ecmascript.org/doku.php?id=harmony:egal ([https](https://wiki.ecmascript.org/doku.php?id=harmony:egal) result ConnectTimeoutException).
* [ ] http://www.w3.org/TR/html4/loose.dtd (ReadTimeoutException) with 1 occurrences migrated to:
  https://www.w3.org/TR/html4/loose.dtd ([https](https://www.w3.org/TR/html4/loose.dtd) result ReadTimeoutException).
* [ ] http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd (ReadTimeoutException) with 24 occurrences migrated to:
  https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd ([https](https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd) result ReadTimeoutException).
* [ ] http://codehaus.org/xstream-parent/xstream/ (UnknownHostException) with 1 occurrences migrated to:
  https://codehaus.org/xstream-parent/xstream/ ([https](https://codehaus.org/xstream-parent/xstream/) result UnknownHostException).
* [ ] http://dist.codehaus.org/janino/new_bsd_license.txt (UnknownHostException) with 1 occurrences migrated to:
  https://dist.codehaus.org/janino/new_bsd_license.txt ([https](https://dist.codehaus.org/janino/new_bsd_license.txt) result UnknownHostException).
* [ ] http://docs.codehaus.org/display/BTM/Home/btm (UnknownHostException) with 1 occurrences migrated to:
  https://docs.codehaus.org/display/BTM/Home/btm ([https](https://docs.codehaus.org/display/BTM/Home/btm) result UnknownHostException).
* [ ] http://docs.codehaus.org/display/JANINO/Home/janino (UnknownHostException) with 1 occurrences migrated to:
  https://docs.codehaus.org/display/JANINO/Home/janino ([https](https://docs.codehaus.org/display/JANINO/Home/janino) result UnknownHostException).
* [ ] http://foo.bar (UnknownHostException) with 14 occurrences migrated to:
  https://foo.bar ([https](https://foo.bar) result UnknownHostException).
* [ ] http://forge.sonatype.com/spice-parent/plexus-build-api/ (UnknownHostException) with 3 occurrences migrated to:
  https://forge.sonatype.com/spice-parent/plexus-build-api/ ([https](https://forge.sonatype.com/spice-parent/plexus-build-api/) result UnknownHostException).
* [ ] http://jaxen.codehaus.org/ (UnknownHostException) with 1 occurrences migrated to:
  https://jaxen.codehaus.org/ ([https](https://jaxen.codehaus.org/) result UnknownHostException).
* [ ] http://jaxen.codehaus.org/license.html (UnknownHostException) with 1 occurrences migrated to:
  https://jaxen.codehaus.org/license.html ([https](https://jaxen.codehaus.org/license.html) result UnknownHostException).
* [ ] http://metrics.codahale.com/metrics-core/ (UnknownHostException) with 1 occurrences migrated to:
  https://metrics.codahale.com/metrics-core/ ([https](https://metrics.codahale.com/metrics-core/) result UnknownHostException).
* [ ] http://metrics.codahale.com/metrics-ganglia/ (UnknownHostException) with 1 occurrences migrated to:
  https://metrics.codahale.com/metrics-ganglia/ ([https](https://metrics.codahale.com/metrics-ganglia/) result UnknownHostException).
* [ ] http://metrics.codahale.com/metrics-graphite/ (UnknownHostException) with 1 occurrences migrated to:
  https://metrics.codahale.com/metrics-graphite/ ([https](https://metrics.codahale.com/metrics-graphite/) result UnknownHostException).
* [ ] http://metrics.codahale.com/metrics-servlets/ (UnknownHostException) with 1 occurrences migrated to:
  https://metrics.codahale.com/metrics-servlets/ ([https](https://metrics.codahale.com/metrics-servlets/) result UnknownHostException).
* [ ] http://mojo.codehaus.org/animal-sniffer-parent/animal-sniffer-maven-plugin (UnknownHostException) with 2 occurrences migrated to:
  https://mojo.codehaus.org/animal-sniffer-parent/animal-sniffer-maven-plugin ([https](https://mojo.codehaus.org/animal-sniffer-parent/animal-sniffer-maven-plugin) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-archiver/ (UnknownHostException) with 3 occurrences migrated to:
  https://plexus.codehaus.org/plexus-archiver/ ([https](https://plexus.codehaus.org/plexus-archiver/) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-classworlds/ (UnknownHostException) with 3 occurrences migrated to:
  https://plexus.codehaus.org/plexus-classworlds/ ([https](https://plexus.codehaus.org/plexus-classworlds/) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-components/plexus-interpolation (UnknownHostException) with 3 occurrences migrated to:
  https://plexus.codehaus.org/plexus-components/plexus-interpolation ([https](https://plexus.codehaus.org/plexus-components/plexus-interpolation) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-containers/plexus-component-annotations/ (UnknownHostException) with 3 occurrences migrated to:
  https://plexus.codehaus.org/plexus-containers/plexus-component-annotations/ ([https](https://plexus.codehaus.org/plexus-containers/plexus-component-annotations/) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-containers/plexus-component-metadata/ (UnknownHostException) with 8 occurrences migrated to:
  https://plexus.codehaus.org/plexus-containers/plexus-component-metadata/ ([https](https://plexus.codehaus.org/plexus-containers/plexus-component-metadata/) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-io/ (UnknownHostException) with 3 occurrences migrated to:
  https://plexus.codehaus.org/plexus-io/ ([https](https://plexus.codehaus.org/plexus-io/) result UnknownHostException).
* [ ] http://plexus.codehaus.org/plexus-utils (UnknownHostException) with 3 occurrences migrated to:
  https://plexus.codehaus.org/plexus-utils ([https](https://plexus.codehaus.org/plexus-utils) result UnknownHostException).
* [ ] http://spice.sonatype.org/plexus-cipher (UnknownHostException) with 3 occurrences migrated to:
  https://spice.sonatype.org/plexus-cipher ([https](https://spice.sonatype.org/plexus-cipher) result UnknownHostException).
* [ ] http://spice.sonatype.org/plexus-sec-dispatcher (UnknownHostException) with 3 occurrences migrated to:
  https://spice.sonatype.org/plexus-sec-dispatcher ([https](https://spice.sonatype.org/plexus-sec-dispatcher) result UnknownHostException).
* [ ] http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html (UnknownHostException) with 1 occurrences migrated to:
  https://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html ([https](https://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html) result UnknownHostException).
* [ ] http://wiki.fasterxml.com/JacksonExtensionXmlDataBinding (UnknownHostException) with 1 occurrences migrated to:
  https://wiki.fasterxml.com/JacksonExtensionXmlDataBinding ([https](https://wiki.fasterxml.com/JacksonExtensionXmlDataBinding) result UnknownHostException).
* [ ] http://wiki.fasterxml.com/JacksonModuleJSR310 (UnknownHostException) with 1 occurrences migrated to:
  https://wiki.fasterxml.com/JacksonModuleJSR310 ([https](https://wiki.fasterxml.com/JacksonModuleJSR310) result UnknownHostException).
* [ ] http://wiki.fasterxml.com/JacksonModuleJoda (UnknownHostException) with 1 occurrences migrated to:
  https://wiki.fasterxml.com/JacksonModuleJoda ([https](https://wiki.fasterxml.com/JacksonModuleJoda) result UnknownHostException).
* [ ] http://www.aspectj.org (UnknownHostException) with 3 occurrences migrated to:
  https://www.aspectj.org ([https](https://www.aspectj.org) result UnknownHostException).
* [ ] http://www.blog.project13.pl (UnknownHostException) with 5 occurrences migrated to:
  https://www.blog.project13.pl ([https](https://www.blog.project13.pl) result UnknownHostException).
* [ ] http://www.mockito.org (UnknownHostException) with 1 occurrences migrated to:
  https://www.mockito.org ([https](https://www.mockito.org) result UnknownHostException).
* [ ] http://activemq.apache.org/activemq-amqp (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-amqp ([https](https://activemq.apache.org/activemq-amqp) result 404).
* [ ] http://activemq.apache.org/activemq-blueprint (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-blueprint ([https](https://activemq.apache.org/activemq-blueprint) result 404).
* [ ] http://activemq.apache.org/activemq-broker (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-broker ([https](https://activemq.apache.org/activemq-broker) result 404).
* [ ] http://activemq.apache.org/activemq-camel (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-camel ([https](https://activemq.apache.org/activemq-camel) result 404).
* [ ] http://activemq.apache.org/activemq-client (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-client ([https](https://activemq.apache.org/activemq-client) result 404).
* [ ] http://activemq.apache.org/activemq-console (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-console ([https](https://activemq.apache.org/activemq-console) result 404).
* [ ] http://activemq.apache.org/activemq-http (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-http ([https](https://activemq.apache.org/activemq-http) result 404).
* [ ] http://activemq.apache.org/activemq-jaas (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-jaas ([https](https://activemq.apache.org/activemq-jaas) result 404).
* [ ] http://activemq.apache.org/activemq-jdbc-store (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-jdbc-store ([https](https://activemq.apache.org/activemq-jdbc-store) result 404).
* [ ] http://activemq.apache.org/activemq-jms-pool (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-jms-pool ([https](https://activemq.apache.org/activemq-jms-pool) result 404).
* [ ] http://activemq.apache.org/activemq-kahadb-store (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-kahadb-store ([https](https://activemq.apache.org/activemq-kahadb-store) result 404).
* [ ] http://activemq.apache.org/activemq-karaf (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-karaf ([https](https://activemq.apache.org/activemq-karaf) result 404).
* [ ] http://activemq.apache.org/activemq-leveldb-store (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-leveldb-store ([https](https://activemq.apache.org/activemq-leveldb-store) result 404).
* [ ] http://activemq.apache.org/activemq-log4j-appender (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-log4j-appender ([https](https://activemq.apache.org/activemq-log4j-appender) result 404).
* [ ] http://activemq.apache.org/activemq-mqtt (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-mqtt ([https](https://activemq.apache.org/activemq-mqtt) result 404).
* [ ] http://activemq.apache.org/activemq-openwire-generator (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-openwire-generator ([https](https://activemq.apache.org/activemq-openwire-generator) result 404).
* [ ] http://activemq.apache.org/activemq-openwire-legacy (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-openwire-legacy ([https](https://activemq.apache.org/activemq-openwire-legacy) result 404).
* [ ] http://activemq.apache.org/activemq-osgi (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-osgi ([https](https://activemq.apache.org/activemq-osgi) result 404).
* [ ] http://activemq.apache.org/activemq-partition (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-partition ([https](https://activemq.apache.org/activemq-partition) result 404).
* [ ] http://activemq.apache.org/activemq-pool (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-pool ([https](https://activemq.apache.org/activemq-pool) result 404).
* [ ] http://activemq.apache.org/activemq-ra (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-ra ([https](https://activemq.apache.org/activemq-ra) result 404).
* [ ] http://activemq.apache.org/activemq-run (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-run ([https](https://activemq.apache.org/activemq-run) result 404).
* [ ] http://activemq.apache.org/activemq-runtime-config (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-runtime-config ([https](https://activemq.apache.org/activemq-runtime-config) result 404).
* [ ] http://activemq.apache.org/activemq-shiro (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-shiro ([https](https://activemq.apache.org/activemq-shiro) result 404).
* [ ] http://activemq.apache.org/activemq-spring (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-spring ([https](https://activemq.apache.org/activemq-spring) result 404).
* [ ] http://activemq.apache.org/activemq-stomp (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-stomp ([https](https://activemq.apache.org/activemq-stomp) result 404).
* [ ] http://activemq.apache.org/activemq-web (404) with 1 occurrences migrated to:
  https://activemq.apache.org/activemq-web ([https](https://activemq.apache.org/activemq-web) result 404).
* [ ] http://apache.org/activemq/artemis-commons (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-commons ([https](https://apache.org/activemq/artemis-commons) result 404).
* [ ] http://apache.org/activemq/artemis-core-client (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-core-client ([https](https://apache.org/activemq/artemis-core-client) result 404).
* [ ] http://apache.org/activemq/artemis-jms-client (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-jms-client ([https](https://apache.org/activemq/artemis-jms-client) result 404).
* [ ] http://apache.org/activemq/artemis-jms-server (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-jms-server ([https](https://apache.org/activemq/artemis-jms-server) result 404).
* [ ] http://apache.org/activemq/artemis-journal (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-journal ([https](https://apache.org/activemq/artemis-journal) result 404).
* [ ] http://apache.org/activemq/artemis-native (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-native ([https](https://apache.org/activemq/artemis-native) result 404).
* [ ] http://apache.org/activemq/artemis-protocols/artemis-amqp-protocol (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-protocols/artemis-amqp-protocol ([https](https://apache.org/activemq/artemis-protocols/artemis-amqp-protocol) result 404).
* [ ] http://apache.org/activemq/artemis-selector (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-selector ([https](https://apache.org/activemq/artemis-selector) result 404).
* [ ] http://apache.org/activemq/artemis-server (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-server ([https](https://apache.org/activemq/artemis-server) result 404).
* [ ] http://apache.org/activemq/artemis-service-extensions (404) with 1 occurrences migrated to:
  https://apache.org/activemq/artemis-service-extensions ([https](https://apache.org/activemq/artemis-service-extensions) result 404).
* [ ] http://camel.apache.org/camel-parent/components/camel-jackson (404) with 1 occurrences migrated to:
  https://camel.apache.org/camel-parent/components/camel-jackson ([https](https://camel.apache.org/camel-parent/components/camel-jackson) result 404).
* [ ] http://camel.apache.org/camel-parent/components/camel-jms (404) with 1 occurrences migrated to:
  https://camel.apache.org/camel-parent/components/camel-jms ([https](https://camel.apache.org/camel-parent/components/camel-jms) result 404).
* [ ] http://camel.apache.org/camel-parent/components/camel-spring (404) with 1 occurrences migrated to:
  https://camel.apache.org/camel-parent/components/camel-spring ([https](https://camel.apache.org/camel-parent/components/camel-spring) result 404).
* [ ] http://camel.apache.org/camel-parent/components/camel-spring-boot-starter (404) with 1 occurrences migrated to:
  https://camel.apache.org/camel-parent/components/camel-spring-boot-starter ([https](https://camel.apache.org/camel-parent/components/camel-spring-boot-starter) result 404).
* [ ] http://code.google.com/appengine/appengine-api-1.0-sdk/ (404) with 1 occurrences migrated to:
  https://code.google.com/appengine/appengine-api-1.0-sdk/ ([https](https://code.google.com/appengine/appengine-api-1.0-sdk/) result 404).
* [ ] http://db.apache.org/derby/derby/ (404) with 1 occurrences migrated to:
  https://db.apache.org/derby/derby/ ([https](https://db.apache.org/derby/derby/) result 404).
* [ ] http://download.eclipse.org/aether/aether-core/0.9.0/apidocs/org/eclipse/aether/util/version/GenericVersionScheme.html (404) with 9 occurrences migrated to:
  https://download.eclipse.org/aether/aether-core/0.9.0/apidocs/org/eclipse/aether/util/version/GenericVersionScheme.html ([https](https://download.eclipse.org/aether/aether-core/0.9.0/apidocs/org/eclipse/aether/util/version/GenericVersionScheme.html) result 404).
* [ ] http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ (301) with 1 occurrences migrated to:
  https://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ ([https](https://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/) result 404).
* [ ] http://flywaydb.org/flyway-core (301) with 1 occurrences migrated to:
  https://flywaydb.org/flyway-core ([https](https://flywaydb.org/flyway-core) result 404).
* [ ] http://github.com/FasterXML/jackson-jaxrs-providers/jackson-datatype-jaxrs (301) with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-jaxrs-providers/jackson-datatype-jaxrs ([https](https://github.com/FasterXML/jackson-jaxrs-providers/jackson-datatype-jaxrs) result 404).
* [ ] http://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base (301) with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base ([https](https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base) result 404).
* [ ] http://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider (301) with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider ([https](https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider) result 404).
* [ ] http://github.com/neo4j/neo4j-ogm/neo4j-ogm-api/ (301) with 1 occurrences migrated to:
  https://github.com/neo4j/neo4j-ogm/neo4j-ogm-api/ ([https](https://github.com/neo4j/neo4j-ogm/neo4j-ogm-api/) result 404).
* [ ] http://github.com/neo4j/neo4j-ogm/neo4j-ogm-compiler/ (301) with 1 occurrences migrated to:
  https://github.com/neo4j/neo4j-ogm/neo4j-ogm-compiler/ ([https](https://github.com/neo4j/neo4j-ogm/neo4j-ogm-compiler/) result 404).
* [ ] http://github.com/neo4j/neo4j-ogm/neo4j-ogm-core/ (301) with 1 occurrences migrated to:
  https://github.com/neo4j/neo4j-ogm/neo4j-ogm-core/ ([https](https://github.com/neo4j/neo4j-ogm/neo4j-ogm-core/) result 404).
* [ ] http://github.com/neo4j/neo4j-ogm/neo4j-ogm-http-driver/ (301) with 1 occurrences migrated to:
  https://github.com/neo4j/neo4j-ogm/neo4j-ogm-http-driver/ ([https](https://github.com/neo4j/neo4j-ogm/neo4j-ogm-http-driver/) result 404).
* [ ] http://github.com/sprinframework (301) with 4 occurrences migrated to:
  https://github.com/sprinframework ([https](https://github.com/sprinframework) result 404).
* [ ] http://github.com/spring_hadoop (301) with 4 occurrences migrated to:
  https://github.com/spring_hadoop ([https](https://github.com/spring_hadoop) result 404).
* [ ] http://google-code-prettify.googlecode.com/svn/trunk/README.html (404) with 1 occurrences migrated to:
  https://google-code-prettify.googlecode.com/svn/trunk/README.html ([https](https://google-code-prettify.googlecode.com/svn/trunk/README.html) result 404).
* [ ] http://hc.apache.org/httpcomponents-asyncclient (404) with 1 occurrences migrated to:
  https://hc.apache.org/httpcomponents-asyncclient ([https](https://hc.apache.org/httpcomponents-asyncclient) result 404).
* [ ] http://hc.apache.org/httpcomponents-client (404) with 5 occurrences migrated to:
  https://hc.apache.org/httpcomponents-client ([https](https://hc.apache.org/httpcomponents-client) result 404).
* [ ] http://hibernate.org/validator/hibernate-validator (404) with 1 occurrences migrated to:
  https://hibernate.org/validator/hibernate-validator ([https](https://hibernate.org/validator/hibernate-validator) result 404).
* [ ] http://hibernate.org/validator/hibernate-validator-annotation-processor (404) with 1 occurrences migrated to:
  https://hibernate.org/validator/hibernate-validator-annotation-processor ([https](https://hibernate.org/validator/hibernate-validator-annotation-processor) result 404).
* [ ] http://hornetq.org/hornetq-jms-client (301) with 1 occurrences migrated to:
  https://hornetq.jboss.org/hornetq-jms-client ([https](https://hornetq.org/hornetq-jms-client) result 404).
* [ ] http://hornetq.org/hornetq-jms-server (301) with 1 occurrences migrated to:
  https://hornetq.jboss.org/hornetq-jms-server ([https](https://hornetq.org/hornetq-jms-server) result 404).
* [ ] http://www.infinispan.org/infinispan-jcache-parent/infinispan-jcache (301) with 1 occurrences migrated to:
  https://infinispan.org/infinispan-jcache-parent/infinispan-jcache ([https](https://www.infinispan.org/infinispan-jcache-parent/infinispan-jcache) result 404).
* [ ] http://www.infinispan.org/infinispan-spring4-parent/infinispan-spring4-common (301) with 1 occurrences migrated to:
  https://infinispan.org/infinispan-spring4-parent/infinispan-spring4-common ([https](https://www.infinispan.org/infinispan-spring4-parent/infinispan-spring4-common) result 404).
* [ ] http://www.infinispan.org/infinispan-spring4-parent/infinispan-spring4-embedded (301) with 1 occurrences migrated to:
  https://infinispan.org/infinispan-spring4-parent/infinispan-spring4-embedded ([https](https://www.infinispan.org/infinispan-spring4-parent/infinispan-spring4-embedded) result 404).
* [ ] http://json.org/json2.js (404) with 1 occurrences migrated to:
  https://json.org/json2.js ([https](https://json.org/json2.js) result 404).
* [ ] http://logback.qos.ch/logback-access (302) with 1 occurrences migrated to:
  https://logback.qos.ch/logback-access ([https](https://logback.qos.ch/logback-access) result 404).
* [ ] http://logback.qos.ch/logback-classic (302) with 1 occurrences migrated to:
  https://logback.qos.ch/logback-classic ([https](https://logback.qos.ch/logback-classic) result 404).
* [ ] http://logback.qos.ch/logback-core (302) with 4 occurrences migrated to:
  https://logback.qos.ch/logback-core ([https](https://logback.qos.ch/logback-core) result 404).
* [ ] http://lucene.apache.org/solr-parent/solr-solrj (404) with 1 occurrences migrated to:
  https://lucene.apache.org/solr-parent/solr-solrj ([https](https://lucene.apache.org/solr-parent/solr-solrj) result 404).
* [ ] http://pivotal.github.com/jasmine (404) with 1 occurrences migrated to:
  https://pivotal.github.com/jasmine ([https](https://pivotal.github.com/jasmine) result 404).
* [ ] http://projects.spring.io/spring-boot/spring-boot-actuator-docs/ (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-boot/spring-boot-actuator-docs/ ([https](https://projects.spring.io/spring-boot/spring-boot-actuator-docs/) result 404).
* [ ] http://projects.spring.io/spring-cloud/spring-cloud-commons/ (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-cloud/spring-cloud-commons/ ([https](https://projects.spring.io/spring-cloud/spring-cloud-commons/) result 404).
* [ ] http://projects.spring.io/spring-cloud/spring-cloud-config-monitor/ (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-cloud/spring-cloud-config-monitor/ ([https](https://projects.spring.io/spring-cloud/spring-cloud-config-monitor/) result 404).
* [ ] http://projects.spring.io/spring-cloud/spring-cloud-config-server/ (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-cloud/spring-cloud-config-server/ ([https](https://projects.spring.io/spring-cloud/spring-cloud-config-server/) result 404).
* [ ] http://projects.spring.io/spring-cloud/spring-cloud-context/ (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-cloud/spring-cloud-context/ ([https](https://projects.spring.io/spring-cloud/spring-cloud-context/) result 404).
* [ ] http://projects.spring.io/spring-data-mongodb/spring-data-mongodb (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-data-mongodb/spring-data-mongodb ([https](https://projects.spring.io/spring-data-mongodb/spring-data-mongodb) result 404).
* [ ] http://projects.spring.io/spring-data-mongodb/spring-data-mongodb-cross-store (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-data-mongodb/spring-data-mongodb-cross-store ([https](https://projects.spring.io/spring-data-mongodb/spring-data-mongodb-cross-store) result 404).
* [ ] http://projects.spring.io/spring-data-mongodb/spring-data-mongodb-log4j (404) with 1 occurrences migrated to:
  https://projects.spring.io/spring-data-mongodb/spring-data-mongodb-log4j ([https](https://projects.spring.io/spring-data-mongodb/spring-data-mongodb-log4j) result 404).
* [ ] http://repo.spring.io/snapshots (404) with 13 occurrences migrated to:
  https://repo.spring.io/snapshots ([https](https://repo.spring.io/snapshots) result 404).
* [ ] http://www.apache.org/license/LICENSE-2.0.txt (404) with 3 occurrences migrated to:
  https://www.apache.org/license/LICENSE-2.0.txt ([https](https://www.apache.org/license/LICENSE-2.0.txt) result 404).
* [ ] http://www.atomikos.com/ate/transactions-jdbc/ (301) with 1 occurrences migrated to:
  https://www.atomikos.com/ate/transactions-jdbc/ ([https](https://www.atomikos.com/ate/transactions-jdbc/) result 404).
* [ ] http://www.atomikos.com/ate/transactions-jms/ (301) with 1 occurrences migrated to:
  https://www.atomikos.com/ate/transactions-jms/ ([https](https://www.atomikos.com/ate/transactions-jms/) result 404).
* [ ] http://www.atomikos.com/ate/transactions-jta/ (301) with 1 occurrences migrated to:
  https://www.atomikos.com/ate/transactions-jta/ ([https](https://www.atomikos.com/ate/transactions-jta/) result 404).
* [ ] http://www.crashub.org/crash.cli/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.cli/ ([https](https://www.crashub.org/crash.cli/) result 404).
* [ ] http://www.crashub.org/crash.connectors/crash.connectors.ssh/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.connectors/crash.connectors.ssh/ ([https](https://www.crashub.org/crash.connectors/crash.connectors.ssh/) result 404).
* [ ] http://www.crashub.org/crash.connectors/crash.connectors.telnet/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.connectors/crash.connectors.telnet/ ([https](https://www.crashub.org/crash.connectors/crash.connectors.telnet/) result 404).
* [ ] http://www.crashub.org/crash.embed/crash.embed.spring/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.embed/crash.embed.spring/ ([https](https://www.crashub.org/crash.embed/crash.embed.spring/) result 404).
* [ ] http://www.crashub.org/crash.plugins/crash.plugins.cron/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.plugins/crash.plugins.cron/ ([https](https://www.crashub.org/crash.plugins/crash.plugins.cron/) result 404).
* [ ] http://www.crashub.org/crash.plugins/crash.plugins.mail/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.plugins/crash.plugins.mail/ ([https](https://www.crashub.org/crash.plugins/crash.plugins.mail/) result 404).
* [ ] http://www.crashub.org/crash.shell/ (404) with 1 occurrences migrated to:
  https://www.crashub.org/crash.shell/ ([https](https://www.crashub.org/crash.shell/) result 404).
* [ ] http://www.eclipse.org/jetty/javax-websocket-server-impl (404) with 1 occurrences migrated to:
  https://www.eclipse.org/jetty/javax-websocket-server-impl ([https](https://www.eclipse.org/jetty/javax-websocket-server-impl) result 404).
* [ ] http://www.eclipse.org/jetty/websocket-client (404) with 1 occurrences migrated to:
  https://www.eclipse.org/jetty/websocket-client ([https](https://www.eclipse.org/jetty/websocket-client) result 404).
* [ ] http://www.eclipse.org/jetty/websocket-server (404) with 1 occurrences migrated to:
  https://www.eclipse.org/jetty/websocket-server ([https](https://www.eclipse.org/jetty/websocket-server) result 404).
* [ ] http://www.eclipse.org/sisu/org.eclipse.sisu.inject/ (404) with 3 occurrences migrated to:
  https://www.eclipse.org/sisu/org.eclipse.sisu.inject/ ([https](https://www.eclipse.org/sisu/org.eclipse.sisu.inject/) result 404).
* [ ] http://www.eclipse.org/sisu/org.eclipse.sisu.plexus/ (404) with 3 occurrences migrated to:
  https://www.eclipse.org/sisu/org.eclipse.sisu.plexus/ ([https](https://www.eclipse.org/sisu/org.eclipse.sisu.plexus/) result 404).
* [ ] http://www.jboss.org/undertow-parent/undertow-core (404) with 1 occurrences migrated to:
  https://www.jboss.org/undertow-parent/undertow-core ([https](https://www.jboss.org/undertow-parent/undertow-core) result 404).
* [ ] http://www.jboss.org/undertow-parent/undertow-servlet (404) with 1 occurrences migrated to:
  https://www.jboss.org/undertow-parent/undertow-servlet ([https](https://www.jboss.org/undertow-parent/undertow-servlet) result 404).
* [ ] http://www.jboss.org/undertow-parent/undertow-websockets-jsr (404) with 1 occurrences migrated to:
  https://www.jboss.org/undertow-parent/undertow-websockets-jsr ([https](https://www.jboss.org/undertow-parent/undertow-websockets-jsr) result 404).
* [ ] http://www.jooq.org/jooq (404) with 1 occurrences migrated to:
  https://www.jooq.org/jooq ([https](https://www.jooq.org/jooq) result 404).
* [ ] http://www.jooq.org/jooq-codegen (404) with 1 occurrences migrated to:
  https://www.jooq.org/jooq-codegen ([https](https://www.jooq.org/jooq-codegen) result 404).
* [ ] http://www.jooq.org/jooq-meta (404) with 1 occurrences migrated to:
  https://www.jooq.org/jooq-meta ([https](https://www.jooq.org/jooq-meta) result 404).
* [ ] http://www.liquibase.org/liquibase-core (404) with 1 occurrences migrated to:
  https://www.liquibase.org/liquibase-core ([https](https://www.liquibase.org/liquibase-core) result 404).
* [ ] http://www.seleniumhq.org/selenium-api/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-api/ ([https](https://www.seleniumhq.org/selenium-api/) result 404).
* [ ] http://www.seleniumhq.org/selenium-chrome-driver/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-chrome-driver/ ([https](https://www.seleniumhq.org/selenium-chrome-driver/) result 404).
* [ ] http://www.seleniumhq.org/selenium-firefox-driver/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-firefox-driver/ ([https](https://www.seleniumhq.org/selenium-firefox-driver/) result 404).
* [ ] http://www.seleniumhq.org/selenium-ie-driver/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-ie-driver/ ([https](https://www.seleniumhq.org/selenium-ie-driver/) result 404).
* [ ] http://www.seleniumhq.org/selenium-java/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-java/ ([https](https://www.seleniumhq.org/selenium-java/) result 404).
* [ ] http://www.seleniumhq.org/selenium-remote-driver/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-remote-driver/ ([https](https://www.seleniumhq.org/selenium-remote-driver/) result 404).
* [ ] http://www.seleniumhq.org/selenium-safari-driver/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-safari-driver/ ([https](https://www.seleniumhq.org/selenium-safari-driver/) result 404).
* [ ] http://www.seleniumhq.org/selenium-support/ (302) with 1 occurrences migrated to:
  https://www.seleniumhq.org/selenium-support/ ([https](https://www.seleniumhq.org/selenium-support/) result 404).
* [ ] http://api.twitter.com/1/geo/id/01fbe706f872cb32.json (403) with 15 occurrences migrated to:
  https://api.twitter.com/1/geo/id/01fbe706f872cb32.json ([https](https://api.twitter.com/1/geo/id/01fbe706f872cb32.json) result 410).

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://apache.org/foundation/contributing.html with 6 occurrences migrated to:
  https://apache.org/foundation/contributing.html ([https](https://apache.org/foundation/contributing.html) result 200).
* [ ] http://args4j.kohsuke.org/args4j/ with 4 occurrences migrated to:
  https://args4j.kohsuke.org/args4j/ ([https](https://args4j.kohsuke.org/args4j/) result 200).
* [ ] http://asciidoctor.org with 2 occurrences migrated to:
  https://asciidoctor.org ([https](https://asciidoctor.org) result 200).
* [ ] http://backbonejs.org with 1 occurrences migrated to:
  https://backbonejs.org ([https](https://backbonejs.org) result 200).
* [ ] http://www.snakeyaml.org (302) with 1 occurrences migrated to:
  https://bitbucket.org/asomov/snakeyaml ([https](https://www.snakeyaml.org) result 200).
* [ ] http://bugs.jquery.com/ticket/12282 with 1 occurrences migrated to:
  https://bugs.jquery.com/ticket/12282 ([https](https://bugs.jquery.com/ticket/12282) result 200).
* [ ] http://bugs.jquery.com/ticket/12359 with 1 occurrences migrated to:
  https://bugs.jquery.com/ticket/12359 ([https](https://bugs.jquery.com/ticket/12359) result 200).
* [ ] http://bugs.jquery.com/ticket/13378 with 1 occurrences migrated to:
  https://bugs.jquery.com/ticket/13378 ([https](https://bugs.jquery.com/ticket/13378) result 200).
* [ ] http://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js with 11 occurrences migrated to:
  https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js ([https](https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js) result 200).
* [ ] http://codehaus-plexus.github.io/plexus-containers/plexus-component-metadata/ with 1 occurrences migrated to:
  https://codehaus-plexus.github.io/plexus-containers/plexus-component-metadata/ ([https](https://codehaus-plexus.github.io/plexus-containers/plexus-component-metadata/) result 200).
* [ ] http://commons.apache.org/proper/commons-beanutils/ with 1 occurrences migrated to:
  https://commons.apache.org/proper/commons-beanutils/ ([https](https://commons.apache.org/proper/commons-beanutils/) result 200).
* [ ] http://commons.apache.org/proper/commons-codec/ with 4 occurrences migrated to:
  https://commons.apache.org/proper/commons-codec/ ([https](https://commons.apache.org/proper/commons-codec/) result 200).
* [ ] http://commons.apache.org/proper/commons-collections/ with 3 occurrences migrated to:
  https://commons.apache.org/proper/commons-collections/ ([https](https://commons.apache.org/proper/commons-collections/) result 200).
* [ ] http://commons.apache.org/proper/commons-compress/ with 3 occurrences migrated to:
  https://commons.apache.org/proper/commons-compress/ ([https](https://commons.apache.org/proper/commons-compress/) result 200).
* [ ] http://commons.apache.org/proper/commons-exec/ with 3 occurrences migrated to:
  https://commons.apache.org/proper/commons-exec/ ([https](https://commons.apache.org/proper/commons-exec/) result 200).
* [ ] http://commons.apache.org/proper/commons-lang/ with 3 occurrences migrated to:
  https://commons.apache.org/proper/commons-lang/ ([https](https://commons.apache.org/proper/commons-lang/) result 200).
* [ ] http://commons.apache.org/proper/commons-pool/ with 1 occurrences migrated to:
  https://commons.apache.org/proper/commons-pool/ ([https](https://commons.apache.org/proper/commons-pool/) result 200).
* [ ] http://community.apache.org/ with 12 occurrences migrated to:
  https://community.apache.org/ ([https](https://community.apache.org/) result 200).
* [ ] http://community.apache.org/gsoc.html with 6 occurrences migrated to:
  https://community.apache.org/gsoc.html ([https](https://community.apache.org/gsoc.html) result 200).
* [ ] http://community.apache.org/newbiefaq.html with 6 occurrences migrated to:
  https://community.apache.org/newbiefaq.html ([https](https://community.apache.org/newbiefaq.html) result 200).
* [ ] http://community.apache.org/newcomers/ with 6 occurrences migrated to:
  https://community.apache.org/newcomers/ ([https](https://community.apache.org/newcomers/) result 200).
* [ ] http://creativecommons.org/licenses/by/3.0/ with 2 occurrences migrated to:
  https://creativecommons.org/licenses/by/3.0/ ([https](https://creativecommons.org/licenses/by/3.0/) result 200).
* [ ] http://diveintohtml5.info/history.html with 1 occurrences migrated to:
  https://diveintohtml5.info/history.html ([https](https://diveintohtml5.info/history.html) result 200).
* [ ] http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/api/ with 1 occurrences migrated to:
  https://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/api/ ([https](https://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/api/) result 200).
* [ ] http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/ with 1 occurrences migrated to:
  https://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/ ([https](https://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/) result 200).
* [ ] http://docs.spring.io/spring-data/jpa/docs/1.4.0.RC1/api/ with 1 occurrences migrated to:
  https://docs.spring.io/spring-data/jpa/docs/1.4.0.RC1/api/ ([https](https://docs.spring.io/spring-data/jpa/docs/1.4.0.RC1/api/) result 200).
* [ ] http://docs.spring.io/spring-data/jpa/docs/1.4.0.RC1/reference/html/ with 1 occurrences migrated to:
  https://docs.spring.io/spring-data/jpa/docs/1.4.0.RC1/reference/html/ ([https](https://docs.spring.io/spring-data/jpa/docs/1.4.0.RC1/reference/html/) result 200).
* [ ] http://example.org with 16 occurrences migrated to:
  https://example.org ([https](https://example.org) result 200).
* [ ] http://example.org/ with 32 occurrences migrated to:
  https://example.org/ ([https](https://example.org/) result 200).
* [ ] http://git-scm.com/ with 5 occurrences migrated to:
  https://git-scm.com/ ([https](https://git-scm.com/) result 200).
* [ ] http://git-scm.com/docs/git-clone with 20 occurrences migrated to:
  https://git-scm.com/docs/git-clone ([https](https://git-scm.com/docs/git-clone) result 200).
* [ ] http://github.com/FasterXML/jackson with 8 occurrences migrated to:
  https://github.com/FasterXML/jackson ([https](https://github.com/FasterXML/jackson) result 200).
* [ ] http://github.com/FasterXML/jackson-dataformats-binary with 2 occurrences migrated to:
  https://github.com/FasterXML/jackson-dataformats-binary ([https](https://github.com/FasterXML/jackson-dataformats-binary) result 200).
* [ ] http://github.com/FasterXML/jackson-datatype-jdk8/ with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-datatype-jdk8/ ([https](https://github.com/FasterXML/jackson-datatype-jdk8/) result 200).
* [ ] http://github.com/FasterXML/jackson-datatype-json-org with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-datatype-json-org ([https](https://github.com/FasterXML/jackson-datatype-json-org) result 200).
* [ ] http://github.com/FasterXML/jackson-module-jaxb-annotations with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-module-jaxb-annotations ([https](https://github.com/FasterXML/jackson-module-jaxb-annotations) result 200).
* [ ] http://github.com/FasterXML/jackson-module-parameter-names/ with 1 occurrences migrated to:
  https://github.com/FasterXML/jackson-module-parameter-names/ ([https](https://github.com/FasterXML/jackson-module-parameter-names/) result 200).
* [ ] http://github.com/dain/snappy with 3 occurrences migrated to:
  https://github.com/dain/snappy ([https](https://github.com/dain/snappy) result 200).
* [ ] http://github.com/mockito/mockito/blob/master/LICENSE with 1 occurrences migrated to:
  https://github.com/mockito/mockito/blob/master/LICENSE ([https](https://github.com/mockito/mockito/blob/master/LICENSE) result 200).
* [ ] http://github.com/samskivert/jmustache with 1 occurrences migrated to:
  https://github.com/samskivert/jmustache ([https](https://github.com/samskivert/jmustache) result 200).
* [ ] http://github.com/skyscreamer/yoga with 4 occurrences migrated to:
  https://github.com/skyscreamer/yoga ([https](https://github.com/skyscreamer/yoga) result 200).
* [ ] http://github.com/spring-cloud/spring-cloud-contract with 1 occurrences migrated to:
  https://github.com/spring-cloud/spring-cloud-contract ([https](https://github.com/spring-cloud/spring-cloud-contract) result 200).
* [ ] http://github.com/spring-projects/gh-pages with 1 occurrences migrated to:
  https://github.com/spring-projects/gh-pages ([https](https://github.com/spring-projects/gh-pages) result 200).
* [ ] http://github.com/spring-projects/spring-data-solr with 1 occurrences migrated to:
  https://github.com/spring-projects/spring-data-solr ([https](https://github.com/spring-projects/spring-data-solr) result 200).
* [ ] http://github.com/spring-projects/spring-security-oauth with 2 occurrences migrated to:
  https://github.com/spring-projects/spring-security-oauth ([https](https://github.com/spring-projects/spring-security-oauth) result 200).
* [ ] http://github.com/tim-group/java-statsd-client with 1 occurrences migrated to:
  https://github.com/tim-group/java-statsd-client ([https](https://github.com/tim-group/java-statsd-client) result 200).
* [ ] http://grigio.org with 1 occurrences migrated to:
  https://grigio.org ([https](https://grigio.org) result 200).
* [ ] http://groovy.github.io/GMavenPlus/ with 10 occurrences migrated to:
  https://groovy.github.io/GMavenPlus/ ([https](https://groovy.github.io/GMavenPlus/) result 200).
* [ ] http://h2database.com/html/license.html with 1 occurrences migrated to:
  https://h2database.com/html/license.html ([https](https://h2database.com/html/license.html) result 200).
* [ ] http://handlebarsjs.com/ with 9 occurrences migrated to:
  https://handlebarsjs.com/ ([https](https://handlebarsjs.com/) result 200).
* [ ] http://helpwanted.apache.org/ with 6 occurrences migrated to:
  https://helpwanted.apache.org/ ([https](https://helpwanted.apache.org/) result 200).
* [ ] http://hibernate.org with 6 occurrences migrated to:
  https://hibernate.org ([https](https://hibernate.org) result 200).
* [ ] http://imgs.mi9.com/uploads/landscape/2101/beautiful-leaf-wallpapers_1280x960_28083.jpg with 1 occurrences migrated to:
  https://imgs.mi9.com/uploads/landscape/2101/beautiful-leaf-wallpapers_1280x960_28083.jpg ([https](https://imgs.mi9.com/uploads/landscape/2101/beautiful-leaf-wallpapers_1280x960_28083.jpg) result 200).
* [ ] http://incubator.apache.org/ with 6 occurrences migrated to:
  https://incubator.apache.org/ ([https](https://incubator.apache.org/) result 200).
* [ ] http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html with 3 occurrences migrated to:
  https://jcp.org/aboutJava/communityprocess/final/jsr250/index.html ([https](https://jcp.org/aboutJava/communityprocess/final/jsr250/index.html) result 200).
* [ ] http://jcp.org/en/jsr/detail?id=914 with 1 occurrences migrated to:
  https://jcp.org/en/jsr/detail?id=914 ([https](https://jcp.org/en/jsr/detail?id=914) result 200).
* [ ] http://jekyllrb.com with 1 occurrences migrated to:
  https://jekyllrb.com ([https](https://jekyllrb.com) result 200).
* [ ] http://jquery.com/ with 6 occurrences migrated to:
  https://jquery.com/ ([https](https://jquery.com/) result 200).
* [ ] http://json.org/license.html with 3 occurrences migrated to:
  https://json.org/license.html ([https](https://json.org/license.html) result 200).
* [ ] http://labs.apache.org/ with 6 occurrences migrated to:
  https://labs.apache.org/ ([https](https://labs.apache.org/) result 200).
* [ ] http://logging.apache.org/log4j/2.x/log4j-api/ with 1 occurrences migrated to:
  https://logging.apache.org/log4j/2.x/log4j-api/ ([https](https://logging.apache.org/log4j/2.x/log4j-api/) result 200).
* [ ] http://logging.apache.org/log4j/2.x/log4j-core/ with 1 occurrences migrated to:
  https://logging.apache.org/log4j/2.x/log4j-core/ ([https](https://logging.apache.org/log4j/2.x/log4j-core/) result 200).
* [ ] http://logging.apache.org/log4j/2.x/log4j-slf4j-impl/ with 1 occurrences migrated to:
  https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/ ([https](https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/) result 200).
* [ ] http://martinfowler.com/articles/consumerDrivenContracts.html with 16 occurrences migrated to:
  https://martinfowler.com/articles/consumerDrivenContracts.html ([https](https://martinfowler.com/articles/consumerDrivenContracts.html) result 200).
* [ ] http://maven.apache.org with 11 occurrences migrated to:
  https://maven.apache.org ([https](https://maven.apache.org) result 200).
* [ ] http://maven.apache.org/ with 134 occurrences migrated to:
  https://maven.apache.org/ ([https](https://maven.apache.org/) result 200).
* [ ] http://maven.apache.org/download.cgi with 14 occurrences migrated to:
  https://maven.apache.org/download.cgi ([https](https://maven.apache.org/download.cgi) result 200).
* [ ] http://maven.apache.org/guides/mini/guide-configuring-plugins.html with 5 occurrences migrated to:
  https://maven.apache.org/guides/mini/guide-configuring-plugins.html ([https](https://maven.apache.org/guides/mini/guide-configuring-plugins.html) result 200).
* [ ] http://maven.apache.org/maven-release/maven-release-plugin/ with 1 occurrences migrated to:
  https://maven.apache.org/maven-release/maven-release-plugin/ ([https](https://maven.apache.org/maven-release/maven-release-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-antrun-plugin/ with 5 occurrences migrated to:
  https://maven.apache.org/plugins/maven-antrun-plugin/ ([https](https://maven.apache.org/plugins/maven-antrun-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-assembly-plugin/ with 5 occurrences migrated to:
  https://maven.apache.org/plugins/maven-assembly-plugin/ ([https](https://maven.apache.org/plugins/maven-assembly-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-checkstyle-plugin/ with 11 occurrences migrated to:
  https://maven.apache.org/plugins/maven-checkstyle-plugin/ ([https](https://maven.apache.org/plugins/maven-checkstyle-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-clean-plugin/ with 5 occurrences migrated to:
  https://maven.apache.org/plugins/maven-clean-plugin/ ([https](https://maven.apache.org/plugins/maven-clean-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-compiler-plugin/ with 7 occurrences migrated to:
  https://maven.apache.org/plugins/maven-compiler-plugin/ ([https](https://maven.apache.org/plugins/maven-compiler-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-dependency-plugin/ with 5 occurrences migrated to:
  https://maven.apache.org/plugins/maven-dependency-plugin/ ([https](https://maven.apache.org/plugins/maven-dependency-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-deploy-plugin/ with 5 occurrences migrated to:
  https://maven.apache.org/plugins/maven-deploy-plugin/ ([https](https://maven.apache.org/plugins/maven-deploy-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-eclipse-plugin/ with 3 occurrences migrated to:
  https://maven.apache.org/plugins/maven-eclipse-plugin/ ([https](https://maven.apache.org/plugins/maven-eclipse-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-install-plugin/ with 5 occurrences migrated to:
  https://maven.apache.org/plugins/maven-install-plugin/ ([https](https://maven.apache.org/plugins/maven-install-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-jar-plugin/ with 8 occurrences migrated to:
  https://maven.apache.org/plugins/maven-jar-plugin/ ([https](https://maven.apache.org/plugins/maven-jar-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-javadoc-plugin/ with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-javadoc-plugin/ ([https](https://maven.apache.org/plugins/maven-javadoc-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-project-info-reports-plugin/ with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-project-info-reports-plugin/ ([https](https://maven.apache.org/plugins/maven-project-info-reports-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-resources-plugin/ with 8 occurrences migrated to:
  https://maven.apache.org/plugins/maven-resources-plugin/ ([https](https://maven.apache.org/plugins/maven-resources-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-shade-plugin/ with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-shade-plugin/ ([https](https://maven.apache.org/plugins/maven-shade-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-site-plugin/ with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-site-plugin/ ([https](https://maven.apache.org/plugins/maven-site-plugin/) result 200).
* [ ] http://maven.apache.org/plugins/maven-source-plugin/ with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-source-plugin/ ([https](https://maven.apache.org/plugins/maven-source-plugin/) result 200).
* [ ] http://maven.apache.org/ref/3.3.9/maven-aether-provider/ with 4 occurrences migrated to:
  https://maven.apache.org/ref/3.3.9/maven-aether-provider/ ([https](https://maven.apache.org/ref/3.3.9/maven-aether-provider/) result 200).
* [ ] http://maven.apache.org/shared/maven-archiver/ with 3 occurrences migrated to:
  https://maven.apache.org/shared/maven-archiver/ ([https](https://maven.apache.org/shared/maven-archiver/) result 200).
* [ ] http://maven.apache.org/shared/maven-filtering/ with 3 occurrences migrated to:
  https://maven.apache.org/shared/maven-filtering/ ([https](https://maven.apache.org/shared/maven-filtering/) result 200).
* [ ] http://maven.apache.org/shared/maven-shared-utils/ with 3 occurrences migrated to:
  https://maven.apache.org/shared/maven-shared-utils/ ([https](https://maven.apache.org/shared/maven-shared-utils/) result 200).
* [ ] http://maven.apache.org/surefire/maven-failsafe-plugin/ with 8 occurrences migrated to:
  https://maven.apache.org/surefire/maven-failsafe-plugin/ ([https](https://maven.apache.org/surefire/maven-failsafe-plugin/) result 200).
* [ ] http://maven.apache.org/surefire/maven-surefire-plugin/ with 6 occurrences migrated to:
  https://maven.apache.org/surefire/maven-surefire-plugin/ ([https](https://maven.apache.org/surefire/maven-surefire-plugin/) result 200).
* [ ] http://maven.apache.org/xsd/assembly-1.1.3.xsd with 34 occurrences migrated to:
  https://maven.apache.org/xsd/assembly-1.1.3.xsd ([https](https://maven.apache.org/xsd/assembly-1.1.3.xsd) result 200).
* [ ] http://maven.apache.org/xsd/maven-4.0.0.xsd with 20 occurrences migrated to:
  https://maven.apache.org/xsd/maven-4.0.0.xsd ([https](https://maven.apache.org/xsd/maven-4.0.0.xsd) result 200).
* [ ] http://nn.mit-license.org/ with 1 occurrences migrated to:
  https://nn.mit-license.org/ ([https](https://nn.mit-license.org/) result 200).
* [ ] http://opensource.org/licenses/BSD-3-Clause with 1 occurrences migrated to:
  https://opensource.org/licenses/BSD-3-Clause ([https](https://opensource.org/licenses/BSD-3-Clause) result 200).
* [ ] http://opensource.org/licenses/MIT with 2 occurrences migrated to:
  https://opensource.org/licenses/MIT ([https](https://opensource.org/licenses/MIT) result 200).
* [ ] http://opensource.org/licenses/mit-license.html with 2 occurrences migrated to:
  https://opensource.org/licenses/mit-license.html ([https](https://opensource.org/licenses/mit-license.html) result 200).
* [ ] http://pages.github.com/ with 1 occurrences migrated to:
  https://pages.github.com/ ([https](https://pages.github.com/) result 200).
* [ ] http://people.apache.org/ with 6 occurrences migrated to:
  https://people.apache.org/ ([https](https://people.apache.org/) result 200).
* [ ] http://people.apache.org/committer-index.html with 6 occurrences migrated to:
  https://people.apache.org/committer-index.html ([https](https://people.apache.org/committer-index.html) result 200).
* [ ] http://projects.spring.io/spring-batch/ with 4 occurrences migrated to:
  https://projects.spring.io/spring-batch/ ([https](https://projects.spring.io/spring-batch/) result 200).
* [ ] http://projects.spring.io/spring-boot/ with 79 occurrences migrated to:
  https://projects.spring.io/spring-boot/ ([https](https://projects.spring.io/spring-boot/) result 200).
* [ ] http://projects.spring.io/spring-cloud/ with 4 occurrences migrated to:
  https://projects.spring.io/spring-cloud/ ([https](https://projects.spring.io/spring-cloud/) result 200).
* [ ] http://repository.jboss.org/licenses/apache-2.0.txt with 3 occurrences migrated to:
  https://repository.jboss.org/licenses/apache-2.0.txt ([https](https://repository.jboss.org/licenses/apache-2.0.txt) result 200).
* [ ] http://rubygems.org with 1 occurrences migrated to:
  https://rubygems.org ([https](https://rubygems.org) result 200).
* [ ] http://rubygems.org/ with 1 occurrences migrated to:
  https://rubygems.org/ ([https](https://rubygems.org/) result 200).
* [ ] http://silviomoreto.github.io/bootstrap-select/ with 2 occurrences migrated to:
  https://silviomoreto.github.io/bootstrap-select/ ([https](https://silviomoreto.github.io/bootstrap-select/) result 200).
* [ ] http://sizzlejs.com/ with 12 occurrences migrated to:
  https://sizzlejs.com/ ([https](https://sizzlejs.com/) result 200).
* [ ] http://spring.io with 1 occurrences migrated to:
  https://spring.io ([https](https://spring.io) result 200).
* [ ] http://spring.io/ with 4 occurrences migrated to:
  https://spring.io/ ([https](https://spring.io/) result 200).
* [ ] http://spring.io/guides/gs/gradle/ with 1 occurrences migrated to:
  https://spring.io/guides/gs/gradle/ ([https](https://spring.io/guides/gs/gradle/) result 200).
* [ ] http://spring.io/guides/gs/maven/ with 1 occurrences migrated to:
  https://spring.io/guides/gs/maven/ ([https](https://spring.io/guides/gs/maven/) result 200).
* [ ] http://spring.io/projects/spring-cloud-contract with 4 occurrences migrated to:
  https://spring.io/projects/spring-cloud-contract ([https](https://spring.io/projects/spring-cloud-contract) result 200).
* [ ] http://stackoverflow.com/questions/tagged/spring-cloud with 1 occurrences migrated to:
  https://stackoverflow.com/questions/tagged/spring-cloud ([https](https://stackoverflow.com/questions/tagged/spring-cloud) result 200).
* [ ] http://stackoverflow.com/questions/tagged/spring-data-jpa with 1 occurrences migrated to:
  https://stackoverflow.com/questions/tagged/spring-data-jpa ([https](https://stackoverflow.com/questions/tagged/spring-data-jpa) result 200).
* [ ] http://status.apache.org with 6 occurrences migrated to:
  https://status.apache.org ([https](https://status.apache.org) result 200).
* [ ] http://subversion.apache.org/style/site.css with 6 occurrences migrated to:
  https://subversion.apache.org/style/site.css ([https](https://subversion.apache.org/style/site.css) result 200).
* [ ] http://tomcat.apache.org/ with 6 occurrences migrated to:
  https://tomcat.apache.org/ ([https](https://tomcat.apache.org/) result 200).
* [ ] http://tomcat.apache.org/taglibs/standard/ with 1 occurrences migrated to:
  https://tomcat.apache.org/taglibs/standard/ ([https](https://tomcat.apache.org/taglibs/standard/) result 200).
* [ ] http://twitter.com/byscuits with 2 occurrences migrated to:
  https://twitter.com/byscuits ([https](https://twitter.com/byscuits) result 200).
* [ ] http://twitter.com/fontawesome with 2 occurrences migrated to:
  https://twitter.com/fontawesome ([https](https://twitter.com/fontawesome) result 200).
* [ ] http://underscorejs.org with 1 occurrences migrated to:
  https://underscorejs.org ([https](https://underscorejs.org) result 200).
* [ ] http://velocity.apache.org/engine/devel/ with 1 occurrences migrated to:
  https://velocity.apache.org/engine/devel/ ([https](https://velocity.apache.org/engine/devel/) result 200).
* [ ] http://velocity.apache.org/tools/devel/ with 1 occurrences migrated to:
  https://velocity.apache.org/tools/devel/ ([https](https://velocity.apache.org/tools/devel/) result 200).
* [ ] http://www.JSON.org/ with 1 occurrences migrated to:
  https://www.JSON.org/ ([https](https://www.JSON.org/) result 200).
* [ ] http://www.antlr.org/ with 1 occurrences migrated to:
  https://www.antlr.org/ ([https](https://www.antlr.org/) result 200).
* [ ] http://www.antlr.org/license.html with 1 occurrences migrated to:
  https://www.antlr.org/license.html ([https](https://www.antlr.org/license.html) result 200).
* [ ] http://www.apachecon.com/ with 6 occurrences migrated to:
  https://www.apachecon.com/ ([https](https://www.apachecon.com/) result 200).
* [ ] http://www.atomikos.com/Main/WhichLicenseApplies with 3 occurrences migrated to:
  https://www.atomikos.com/Main/WhichLicenseApplies ([https](https://www.atomikos.com/Main/WhichLicenseApplies) result 200).
* [ ] http://www.minidev.net/ (301) with 3 occurrences migrated to:
  https://www.bahis41.com/ ([https](https://www.minidev.net/) result 200).
* [ ] http://www.brics.dk/automaton/ with 3 occurrences migrated to:
  https://www.brics.dk/automaton/ ([https](https://www.brics.dk/automaton/) result 200).
* [ ] http://www.eclipse.org/legal/epl-v10.html with 41 occurrences migrated to:
  https://www.eclipse.org/legal/epl-v10.html ([https](https://www.eclipse.org/legal/epl-v10.html) result 200).
* [ ] http://www.eclipse.org/org/documents/epl-v10.php with 21 occurrences migrated to:
  https://www.eclipse.org/org/documents/epl-v10.php ([https](https://www.eclipse.org/org/documents/epl-v10.php) result 200).
* [ ] http://ehcache.org (301) with 4 occurrences migrated to:
  https://www.ehcache.org/ ([https](https://ehcache.org) result 200).
* [ ] http://www.firebirdsql.org/en/devel-jdbc-driver/ with 3 occurrences migrated to:
  https://www.firebirdsql.org/en/devel-jdbc-driver/ ([https](https://www.firebirdsql.org/en/devel-jdbc-driver/) result 200).
* [ ] http://www.gnu.org/copyleft/lesser.html with 5 occurrences migrated to:
  https://www.gnu.org/copyleft/lesser.html ([https](https://www.gnu.org/copyleft/lesser.html) result 200).
* [ ] http://www.gnu.org/licenses/lgpl-2.1.html with 11 occurrences migrated to:
  https://www.gnu.org/licenses/lgpl-2.1.html ([https](https://www.gnu.org/licenses/lgpl-2.1.html) result 200).
* [ ] http://www.gnu.org/licenses/licenses.html with 1 occurrences migrated to:
  https://www.gnu.org/licenses/licenses.html ([https](https://www.gnu.org/licenses/licenses.html) result 200).
* [ ] http://www.gnu.org/licenses/old-licenses/gpl-2.0.html with 1 occurrences migrated to:
  https://www.gnu.org/licenses/old-licenses/gpl-2.0.html ([https](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) result 200).
* [ ] http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html with 5 occurrences migrated to:
  https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html ([https](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) result 200).
* [ ] http://www.gravatar.com/avatar/035ae18f5f948ab3c8a5cbf40bbea383?d=mm&s=60 with 5 occurrences migrated to:
  https://www.gravatar.com/avatar/035ae18f5f948ab3c8a5cbf40bbea383?d=mm&s=60 ([https](https://www.gravatar.com/avatar/035ae18f5f948ab3c8a5cbf40bbea383?d=mm&s=60) result 200).
* [ ] http://www.gravatar.com/avatar/b7639ec84d2cddb7acb11672403edac5?d=mm&s=60 with 5 occurrences migrated to:
  https://www.gravatar.com/avatar/b7639ec84d2cddb7acb11672403edac5?d=mm&s=60 ([https](https://www.gravatar.com/avatar/b7639ec84d2cddb7acb11672403edac5?d=mm&s=60) result 200).
* [ ] http://www.gravatar.com/avatar/cbbca82404e50c3e504384568456197a?d=mm&s=60 with 5 occurrences migrated to:
  https://www.gravatar.com/avatar/cbbca82404e50c3e504384568456197a?d=mm&s=60 ([https](https://www.gravatar.com/avatar/cbbca82404e50c3e504384568456197a?d=mm&s=60) result 200).
* [ ] http://www.h2database.com with 1 occurrences migrated to:
  https://www.h2database.com ([https](https://www.h2database.com) result 200).
* [ ] http://www.javassist.org/ with 1 occurrences migrated to:
  https://www.javassist.org/ ([https](https://www.javassist.org/) result 200).
* [ ] http://www.jboss.org with 2 occurrences migrated to:
  https://www.jboss.org ([https](https://www.jboss.org) result 200).
* [ ] http://www.joda.org/joda-time/ with 1 occurrences migrated to:
  https://www.joda.org/joda-time/ ([https](https://www.joda.org/joda-time/) result 200).
* [ ] http://www.jooq.org/inc/LICENSE.txt with 3 occurrences migrated to:
  https://www.jooq.org/inc/LICENSE.txt ([https](https://www.jooq.org/inc/LICENSE.txt) result 200).
* [ ] http://kyruus.com (301) with 2 occurrences migrated to:
  https://www.kyruus.com/ ([https](https://kyruus.com) result 200).
* [ ] http://www.postgresql.org/about/licence/ with 1 occurrences migrated to:
  https://www.postgresql.org/about/licence/ ([https](https://www.postgresql.org/about/licence/) result 200).
* [ ] http://www.slf4j.org with 16 occurrences migrated to:
  https://www.slf4j.org ([https](https://www.slf4j.org) result 200).
* [ ] http://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura with 16 occurrences migrated to:
  https://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura ([https](https://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura) result 200).
* [ ] http://www.springframework.org/schema/beans/spring-beans.xsd with 18 occurrences migrated to:
  https://www.springframework.org/schema/beans/spring-beans.xsd ([https](https://www.springframework.org/schema/beans/spring-beans.xsd) result 200).
* [ ] http://www.springframework.org/schema/integration/spring-integration.xsd with 18 occurrences migrated to:
  https://www.springframework.org/schema/integration/spring-integration.xsd ([https](https://www.springframework.org/schema/integration/spring-integration.xsd) result 200).
* [ ] http://www.thymeleaf.org with 5 occurrences migrated to:
  https://www.thymeleaf.org ([https](https://www.thymeleaf.org) result 200).
* [ ] http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html with 1 occurrences migrated to:
  https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html ([https](https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html) result 200).
* [ ] http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/java-binding.zip with 1 occurrences migrated to:
  https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/java-binding.zip ([https](https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/java-binding.zip) result 200).
* [ ] http://www.w3.org/TR/2011/REC-css3-selectors-20110929/ with 2 occurrences migrated to:
  https://www.w3.org/TR/2011/REC-css3-selectors-20110929/ ([https](https://www.w3.org/TR/2011/REC-css3-selectors-20110929/) result 200).
* [ ] http://www.w3.org/TR/CSS21/syndata.html with 2 occurrences migrated to:
  https://www.w3.org/TR/CSS21/syndata.html ([https](https://www.w3.org/TR/CSS21/syndata.html) result 200).
* [ ] http://www.w3.org/TR/selectors/ with 4 occurrences migrated to:
  https://www.w3.org/TR/selectors/ ([https](https://www.w3.org/TR/selectors/) result 200).
* [ ] http://www.xmlunit.org/ with 6 occurrences migrated to:
  https://www.xmlunit.org/ ([https](https://www.xmlunit.org/) result 200).
* [ ] http://asm.objectweb.org/asm/ (301) with 3 occurrences migrated to:
  https://asm.ow2.org/asm/ ([https](https://asm.objectweb.org/asm/) result 301).
* [ ] http://asm.objectweb.org/license.html (301) with 2 occurrences migrated to:
  https://asm.ow2.org/license.html ([https](https://asm.objectweb.org/license.html) result 301).
* [ ] http://cloud.spring.io/spring-cloud-cli with 14 occurrences migrated to:
  https://cloud.spring.io/spring-cloud-cli ([https](https://cloud.spring.io/spring-cloud-cli) result 301).
* [ ] http://code.google.com/appengine/terms.html with 1 occurrences migrated to:
  https://code.google.com/appengine/terms.html ([https](https://code.google.com/appengine/terms.html) result 301).
* [ ] http://code.google.com/p/atinject/ with 4 occurrences migrated to:
  https://code.google.com/p/atinject/ ([https](https://code.google.com/p/atinject/) result 301).
* [ ] http://code.google.com/p/guava-libraries/guava with 4 occurrences migrated to:
  https://code.google.com/p/guava-libraries/guava ([https](https://code.google.com/p/guava-libraries/guava) result 301).
* [ ] http://code.google.com/p/json-simple/ with 1 occurrences migrated to:
  https://code.google.com/p/json-simple/ ([https](https://code.google.com/p/json-simple/) result 301).
* [ ] http://codearte.github.io/accurest with 2 occurrences migrated to:
  https://codearte.github.io/accurest ([https](https://codearte.github.io/accurest) result 301).
* [ ] http://couchbase.com with 1 occurrences migrated to:
  https://couchbase.com ([https](https://couchbase.com) result 301).
* [ ] http://curator.apache.org/curator-framework with 1 occurrences migrated to:
  https://curator.apache.org/curator-framework ([https](https://curator.apache.org/curator-framework) result 301).
* [ ] http://curator.apache.org/curator-recipes with 1 occurrences migrated to:
  https://curator.apache.org/curator-recipes ([https](https://curator.apache.org/curator-recipes) result 301).
* [ ] http://curator.apache.org/curator-test with 1 occurrences migrated to:
  https://curator.apache.org/curator-test ([https](https://curator.apache.org/curator-test) result 301).
* [ ] http://curator.apache.org/curator-x-discovery with 1 occurrences migrated to:
  https://curator.apache.org/curator-x-discovery ([https](https://curator.apache.org/curator-x-discovery) result 301).
* [ ] http://dev.w3.org/csswg/cssom/ with 1 occurrences migrated to:
  https://dev.w3.org/csswg/cssom/ ([https](https://dev.w3.org/csswg/cssom/) result 301).
* [ ] http://docs.python.org/library/functions.html with 1 occurrences migrated to:
  https://docs.python.org/library/functions.html ([https](https://docs.python.org/library/functions.html) result 301).
* [ ] http://static.springframework.org/spring-security/oauth/spring-security-oauth (301) with 1 occurrences migrated to:
  https://docs.spring.io/spring-security/oauth/spring-security-oauth ([https](https://static.springframework.org/spring-security/oauth/spring-security-oauth) result 301).
* [ ] http://static.springframework.org/spring-security/oauth/spring-security-oauth2 (301) with 1 occurrences migrated to:
  https://docs.spring.io/spring-security/oauth/spring-security-oauth2 ([https](https://static.springframework.org/spring-security/oauth/spring-security-oauth2) result 301).
* [ ] http://dom4j.org with 1 occurrences migrated to:
  https://dom4j.org ([https](https://dom4j.org) result 301).
* [ ] http://el-spec.java.net with 1 occurrences migrated to:
  https://el-spec.java.net ([https](https://el-spec.java.net) result 301).
* [ ] http://forum.spring.io/forum/spring-projects/data with 1 occurrences migrated to:
  https://forum.spring.io/forum/spring-projects/data ([https](https://forum.spring.io/forum/spring-projects/data) result 301).
* [ ] http://git-scm.com/documentation with 10 occurrences migrated to:
  https://git-scm.com/documentation ([https](https://git-scm.com/documentation) result 301).
* [ ] http://github.com/SpringSource/spring-data-envers with 1 occurrences migrated to:
  https://github.com/SpringSource/spring-data-envers ([https](https://github.com/SpringSource/spring-data-envers) result 301).
* [ ] http://github.com/SpringSource/spring-data-jpa with 1 occurrences migrated to:
  https://github.com/SpringSource/spring-data-jpa ([https](https://github.com/SpringSource/spring-data-jpa) result 301).
* [ ] http://github.com/SpringSource/spring-hateoas with 1 occurrences migrated to:
  https://github.com/SpringSource/spring-hateoas ([https](https://github.com/SpringSource/spring-hateoas) result 301).
* [ ] http://github.com/cowtowncoder/java-classmate with 1 occurrences migrated to:
  https://github.com/cowtowncoder/java-classmate ([https](https://github.com/cowtowncoder/java-classmate) result 301).
* [ ] http://github.com/flapdoodle-oss/embedmongo.flapdoodle.de with 1 occurrences migrated to:
  https://github.com/flapdoodle-oss/embedmongo.flapdoodle.de ([https](https://github.com/flapdoodle-oss/embedmongo.flapdoodle.de) result 301).
* [ ] http://github.com/pivotal/jasmine-ajax with 1 occurrences migrated to:
  https://github.com/pivotal/jasmine-ajax ([https](https://github.com/pivotal/jasmine-ajax) result 301).
* [ ] http://glassfish.java.net/public/CDDL+GPL_1_1.html with 16 occurrences migrated to:
  https://glassfish.java.net/public/CDDL+GPL_1_1.html ([https](https://glassfish.java.net/public/CDDL+GPL_1_1.html) result 301).
* [ ] http://hc.apache.org/httpcomponents-core-ga with 4 occurrences migrated to:
  https://hc.apache.org/httpcomponents-core-ga ([https](https://hc.apache.org/httpcomponents-core-ga) result 301).
* [ ] http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_ with 1 occurrences migrated to:
  https://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_ ([https](https://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_) result 301).
* [ ] http://javamail.java.net/javax.mail with 1 occurrences migrated to:
  https://javamail.java.net/javax.mail ([https](https://javamail.java.net/javax.mail) result 301).
* [ ] http://javamail.java.net/javax.mail-api with 1 occurrences migrated to:
  https://javamail.java.net/javax.mail-api ([https](https://javamail.java.net/javax.mail-api) result 301).
* [ ] http://jira.springsource.org/browse/DATAJPA with 1 occurrences migrated to:
  https://jira.springsource.org/browse/DATAJPA ([https](https://jira.springsource.org/browse/DATAJPA) result 301).
* [ ] http://jquery.org/license with 12 occurrences migrated to:
  https://jquery.org/license ([https](https://jquery.org/license) result 301).
* [ ] http://jta-spec.java.net with 1 occurrences migrated to:
  https://jta-spec.java.net ([https](https://jta-spec.java.net) result 301).
* [ ] http://junit.org with 4 occurrences migrated to:
  https://junit.org ([https](https://junit.org) result 301).
* [ ] http://maven.apache.org/enforcer/maven-enforcer-plugin with 4 occurrences migrated to:
  https://maven.apache.org/enforcer/maven-enforcer-plugin ([https](https://maven.apache.org/enforcer/maven-enforcer-plugin) result 301).
* [ ] http://maven.apache.org/plugin-tools/maven-plugin-annotations with 3 occurrences migrated to:
  https://maven.apache.org/plugin-tools/maven-plugin-annotations ([https](https://maven.apache.org/plugin-tools/maven-plugin-annotations) result 301).
* [ ] http://maven.apache.org/plugin-tools/maven-plugin-plugin with 14 occurrences migrated to:
  https://maven.apache.org/plugin-tools/maven-plugin-plugin ([https](https://maven.apache.org/plugin-tools/maven-plugin-plugin) result 301).
* [ ] http://maven.apache.org/plugins/maven-release-plugin/ with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-release-plugin/ ([https](https://maven.apache.org/plugins/maven-release-plugin/) result 301).
* [ ] http://maven.apache.org/plugins/maven-scm-publish-plugin with 4 occurrences migrated to:
  https://maven.apache.org/plugins/maven-scm-publish-plugin ([https](https://maven.apache.org/plugins/maven-scm-publish-plugin) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-artifact with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-artifact ([https](https://maven.apache.org/ref/3.2.5/maven-artifact) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-compat with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-compat ([https](https://maven.apache.org/ref/3.2.5/maven-compat) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-core with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-core ([https](https://maven.apache.org/ref/3.2.5/maven-core) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-model with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-model ([https](https://maven.apache.org/ref/3.2.5/maven-model) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-model-builder with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-model-builder ([https](https://maven.apache.org/ref/3.2.5/maven-model-builder) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-plugin-api with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-plugin-api ([https](https://maven.apache.org/ref/3.2.5/maven-plugin-api) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-repository-metadata with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-repository-metadata ([https](https://maven.apache.org/ref/3.2.5/maven-repository-metadata) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-settings with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-settings ([https](https://maven.apache.org/ref/3.2.5/maven-settings) result 301).
* [ ] http://maven.apache.org/ref/3.2.5/maven-settings-builder with 3 occurrences migrated to:
  https://maven.apache.org/ref/3.2.5/maven-settings-builder ([https](https://maven.apache.org/ref/3.2.5/maven-settings-builder) result 301).
* [ ] http://maven.apache.org/surefire/maven-surefire-plugin with 2 occurrences migrated to:
  https://maven.apache.org/surefire/maven-surefire-plugin ([https](https://maven.apache.org/surefire/maven-surefire-plugin) result 301).
* [ ] http://maven.apache.org/wagon/wagon-provider-api with 3 occurrences migrated to:
  https://maven.apache.org/wagon/wagon-provider-api ([https](https://maven.apache.org/wagon/wagon-provider-api) result 301).
* [ ] http://pact.io/ with 5 occurrences migrated to:
  https://pact.io/ ([https](https://pact.io/) result 301).
* [ ] http://projects.spring.io/spring-cloud with 26 occurrences migrated to:
  https://projects.spring.io/spring-cloud ([https](https://projects.spring.io/spring-cloud) result 301).
* [ ] http://projects.spring.io/spring-data-jpa with 2 occurrences migrated to:
  https://projects.spring.io/spring-data-jpa ([https](https://projects.spring.io/spring-data-jpa) result 301).
* [ ] http://projects.spring.io/spring-ws with 4 occurrences migrated to:
  https://projects.spring.io/spring-ws ([https](https://projects.spring.io/spring-ws) result 301).
* [ ] http://servlet-spec.java.net with 1 occurrences migrated to:
  https://servlet-spec.java.net ([https](https://servlet-spec.java.net) result 301).
* [ ] http://sf.net/projects/wsdl4j with 1 occurrences migrated to:
  https://sf.net/projects/wsdl4j ([https](https://sf.net/projects/wsdl4j) result 301).
* [ ] http://spockframework.github.io/ with 16 occurrences migrated to:
  https://spockframework.github.io/ ([https](https://spockframework.github.io/) result 301).
* [ ] http://spring-projects.github.io/ with 1 occurrences migrated to:
  https://spring-projects.github.io/ ([https](https://spring-projects.github.io/) result 301).
* [ ] http://stackoverflow.com/questions/136443/why-doesnt-ie7- with 1 occurrences migrated to:
  https://stackoverflow.com/questions/136443/why-doesnt-ie7- ([https](https://stackoverflow.com/questions/136443/why-doesnt-ie7-) result 301).
* [ ] http://twitter.github.com/bootstrap/javascript.html with 13 occurrences migrated to:
  https://twitter.github.com/bootstrap/javascript.html ([https](https://twitter.github.com/bootstrap/javascript.html) result 301).
* [ ] http://www.eclemma.org/jacoco with 24 occurrences migrated to:
  https://www.eclemma.org/jacoco ([https](https://www.eclemma.org/jacoco) result 301).
* [ ] http://www.eclipse.org/jetty with 16 occurrences migrated to:
  https://www.eclipse.org/jetty ([https](https://www.eclipse.org/jetty) result 301).
* [ ] http://www.eclipse.org/jetty/jetty-orbit/javax.servlet.jsp with 1 occurrences migrated to:
  https://www.eclipse.org/jetty/jetty-orbit/javax.servlet.jsp ([https](https://www.eclipse.org/jetty/jetty-orbit/javax.servlet.jsp) result 301).
* [ ] http://www.hazelcast.com/hazelcast-hibernate/hazelcast-hibernate4/ with 1 occurrences migrated to:
  https://www.hazelcast.com/hazelcast-hibernate/hazelcast-hibernate4/ ([https](https://www.hazelcast.com/hazelcast-hibernate/hazelcast-hibernate4/) result 301).
* [ ] http://www.hazelcast.com/hazelcast-spring/ with 1 occurrences migrated to:
  https://www.hazelcast.com/hazelcast-spring/ ([https](https://www.hazelcast.com/hazelcast-spring/) result 301).
* [ ] http://www.hazelcast.com/hazelcast/ with 1 occurrences migrated to:
  https://www.hazelcast.com/hazelcast/ ([https](https://www.hazelcast.com/hazelcast/) result 301).
* [ ] http://www.jolokia.org/jolokia-agent-parent/jolokia-core/ with 1 occurrences migrated to:
  https://www.jolokia.org/jolokia-agent-parent/jolokia-core/ ([https](https://www.jolokia.org/jolokia-agent-parent/jolokia-core/) result 301).
* [ ] http://www.modernizr.com/ with 1 occurrences migrated to:
  https://www.modernizr.com/ ([https](https://www.modernizr.com/) result 301).
* [ ] http://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin with 1 occurrences migrated to:
  https://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin ([https](https://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin) result 301).
* [ ] http://www.mojohaus.org/exec-maven-plugin with 5 occurrences migrated to:
  https://www.mojohaus.org/exec-maven-plugin ([https](https://www.mojohaus.org/exec-maven-plugin) result 301).
* [ ] http://www.mongodb.org with 2 occurrences migrated to:
  https://www.mongodb.org ([https](https://www.mongodb.org) result 301).
* [ ] http://www.mozilla.org/MPL/MPL-1.1.html with 1 occurrences migrated to:
  https://www.mozilla.org/MPL/MPL-1.1.html ([https](https://www.mozilla.org/MPL/MPL-1.1.html) result 301).
* [ ] http://www.mozilla.org/js/language/js20/rationale/syntax.html with 1 occurrences migrated to:
  https://www.mozilla.org/js/language/js20/rationale/syntax.html ([https](https://www.mozilla.org/js/language/js20/rationale/syntax.html) result 301).
* [ ] http://www.opensource.org/licenses/BSD-3-Clause with 3 occurrences migrated to:
  https://www.opensource.org/licenses/BSD-3-Clause ([https](https://www.opensource.org/licenses/BSD-3-Clause) result 301).
* [ ] http://www.opensource.org/licenses/bsd-license.php with 8 occurrences migrated to:
  https://www.opensource.org/licenses/bsd-license.php ([https](https://www.opensource.org/licenses/bsd-license.php) result 301).
* [ ] http://www.opensource.org/licenses/cpl1.0.txt with 1 occurrences migrated to:
  https://www.opensource.org/licenses/cpl1.0.txt ([https](https://www.opensource.org/licenses/cpl1.0.txt) result 301).
* [ ] http://www.opensource.org/licenses/lgpl-license.php with 7 occurrences migrated to:
  https://www.opensource.org/licenses/lgpl-license.php ([https](https://www.opensource.org/licenses/lgpl-license.php) result 301).
* [ ] http://www.opensource.org/licenses/mit-license.php with 15 occurrences migrated to:
  https://www.opensource.org/licenses/mit-license.php ([https](https://www.opensource.org/licenses/mit-license.php) result 301).
* [ ] http://www.sonatype.org/nexus/ with 7 occurrences migrated to:
  https://www.sonatype.org/nexus/ ([https](https://www.sonatype.org/nexus/) result 301).
* [ ] http://nexus.sonatype.org/oss-repository-hosting.html/sqlite-jdbc (301) with 1 occurrences migrated to:
  https://www.sonatype.org/nexus/ ([https](https://nexus.sonatype.org/oss-repository-hosting.html/sqlite-jdbc) result 301).
* [ ] http://nexus.sonatype.org/oss-repository-hosting.html/parent/elasticsearch (301) with 1 occurrences migrated to:
  https://www.sonatype.org/nexus/ ([https](https://nexus.sonatype.org/oss-repository-hosting.html/parent/elasticsearch) result 301).
* [ ] http://www.spring.io with 8 occurrences migrated to:
  https://www.spring.io ([https](https://www.spring.io) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-commons with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-commons ([https](https://www.spring.io/spring-data/spring-data-commons) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-gemfire with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-gemfire ([https](https://www.spring.io/spring-data/spring-data-gemfire) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-keyvalue with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-keyvalue ([https](https://www.spring.io/spring-data/spring-data-keyvalue) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-neo4j-parent/spring-data-neo4j with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-neo4j-parent/spring-data-neo4j ([https](https://www.spring.io/spring-data/spring-data-neo4j-parent/spring-data-neo4j) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-redis with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-redis ([https](https://www.spring.io/spring-data/spring-data-redis) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core ([https](https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-hal-browser with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-hal-browser ([https](https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-hal-browser) result 301).
* [ ] http://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc with 1 occurrences migrated to:
  https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc ([https](https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) result 301).
* [ ] http://www.springsource.org with 1 occurrences migrated to:
  https://www.springsource.org ([https](https://www.springsource.org) result 301).
* [ ] http://www.svnkit.com/ with 1 occurrences migrated to:
  https://www.svnkit.com/ ([https](https://www.svnkit.com/) result 301).
* [ ] http://www.svnkit.com/license.html with 1 occurrences migrated to:
  https://www.svnkit.com/license.html ([https](https://www.svnkit.com/license.html) result 301).
* [ ] http://www.w3.org/TR/css3-selectors/ with 2 occurrences migrated to:
  https://www.w3.org/TR/css3-selectors/ ([https](https://www.w3.org/TR/css3-selectors/) result 301).
* [ ] http://www.w3.org/TR/css3-syntax/ with 1 occurrences migrated to:
  https://www.w3.org/TR/css3-syntax/ ([https](https://www.w3.org/TR/css3-syntax/) result 301).
* [ ] http://xml.apache.org/commons/components/external/ with 1 occurrences migrated to:
  https://xml.apache.org/commons/components/external/ ([https](https://xml.apache.org/commons/components/external/) result 301).
* [ ] http://assertj.org/assertj-core (302) with 4 occurrences migrated to:
  https://c.clickprotects.com/c?pid=11165151-assertjorg&c=b25a569aad684a3b19b4ddb1957a8597-wFUeFFewseuf%09XIjEvtaDIvv0%2FfeueSeXpVphrp%09wSSFSuukS3S3f%09ukf%09UwU%09w4%09tijv2I%092vvRN%3A%2F%2FatYIaeIAj0eEtQ%2FatYIa%2FwF%2Ff%2Fw3%2F9Iji6ajQI%2F9Iji6ajQIeEQR%3FAQ%3DkwF%265p6g9%3D3UF3s%26jtL%3D%25Spw%26mIaE2jLvg9%3DSw3F3k%26EajniIa_qo%3DSw3F3k%26oIjigo%3DGLSyb8PsMmzmpfT7CcGAMz%25F9%25F9%26Wai%3D2vvR%25Fh%25s6%25s6EiqEHNIaYIeojavNIjaE2eLIv%25s6iqLH%25s6EiqEH%25F6iqo%25F93s4fffkfuw3fss3Ff%25sUoN_N_Hnzqo%25F9Su4fffffk3Fw3fuUF%25sUoN_N_qLYILvta0_OIIo_qo%25F9344fffffffwffsf4s%25sUoN_I_RatoWEv_qo%25F9mlF3sVV%25s6h%25sUEq_EWNvtQIa_qo%25F9wffws3s%25sUEq_ENI_qo%25F9wffk%25sUEq_OIIo_qo%25F9wffkS3w%25sUoN_I_RatoWEv_QIaE2jLv_qo%25F9wF4u4uk%25sUoN_I_RatoWEv_EtWLva0%25F9Gr%25sUoN_I_RatoWEv_ijLzWjzI%25F9IL%25sUoN_I_RatoWEv_E2jLLIi%25F9tLiqLI%25sUoN_Wai_Y%25F9s%25sUoN_oINv_Wai%25F92vvRN%25Fh%25s6%25s6nnneYIaq.tLnqaIiINNeEtQ%25s6NQjavR2tLIN%25s6jRRiI-qR2tLI-ba%25s6%25sSF6NHW%25sSF9NHWFw3fsSf%25sSsUEQR%25sSF9ENI-r2tRRqLz-mlF3sVV%25s6h%25sSsUEQR%25sSF97rp-7-BZ-DTD-X-h7-DTDp-DTDp-s7fcyf-cy-p5hJ-mlF3sVV%25s6h%25sSsUEYtNaE%25sSF9ENIep5hJemlF3sVV%25s6h%25sSsUEYt_Eaqo%25sSF9%25sS45EjQRjqzL%25sS49%25sU2vvRN%25Fh%25s6%25s6veQ0YqNWjiqMeLIv%25s6EiqEH_RqbIi%25F6Iv%25F9E%25sUjzt%25F9sws%25sUjt%25F9uSU%25sUjEj%25F9%25S5*7jQRjqzLg9*%25S9%25sUNq%25F9-s%25sUEq%25F9-s%25sURq%25F9-s%25sUjo%25F9%25S5*hozatWRg9*%25S9%25sUNYw%25F9%25S5*lajEHIag9*%25S9%25sUjoYv%25F9-s%25sUE2Li%25F9-s%25sUYLoa%25F9ww3S%25sUN.%25F9USUw%25sUaIo%25F9%26iqLHqL_qo%3Duwwsu3f%26gNNov%3Dw3fFsUfksfS3%26NIjaE2g9%3DRsSeF4wEwOFFEwksAOkIOkwO%269IjiDjQI%3DhRRiI%257s%25hp%2Bqc2tLI%257s%25hp%2ByX%2Bwsu85%2BqL%2BJIiitn%26oiRaE%3D433e33%26hX%3Ds%26D8%3Dw%26D9c%3Dsf%26cD%3Dw%26rl%3D4%266cl%3D9rc%26D9r%3D%26Dmr%3D%26mXr%3D%26c9%3D%26AaLgo%3Dwk4UF%26gN6va%3Df%26gNrQjav%3Df%26tR%3D%267m%3D%26XX%3Ds%26gNVRN%3Df%26EtoI%3D%26jEtoI%3DkFU%26EjvIzta0%3D%26BjNVqLH%3D%26D9%3D%26mD%3D%268X%3D%26iLHgo%3D%26rdG%3DmlF3sVV%25s6h%26tR%3D%24%21%7BEiqEH9jvjeEiqEHgo%7D%09%5BLWii%5D%09fefF%09feUs%09%5BLWii%5D%09OjiNI%09uFk%09Sw3F3k%2F3UF3s%2Fuwwsu3f%09rphX7B%09%5BLWii%5D%09%5BLWii%5D%09f%09w3sewUuewffewsu%09%5BLWii%5D%09%5BLWii%5D%09f%7Ef%093wUEkjFj-3EAs-kUAA-uokO-SoA3IjuIjE33%09jNNIav1%09%5BLWii%5D%09jNNIav1%09%5BLWii%5D%09%5BLWii%5D%09%5BLWii%5D%09f%09f%09%5BLWii%5D%09f%09f%09%5BLWii%5D%09GLSyb8PsMmzmpfT7CcGAMz%3D%3D%09%5BLWii%5D%09uFk-Sw3F3k%2F3UF3s%2Fuwwsu3f%09f%09%5BLWii%5D%09%5BLWii%5D%09UUfUs%09jNNIav1taz%09%5BLWii%5D%09%5BLWii%5D%09&r=1&ptt= ([https](https://assertj.org/assertj-core) result 302).
* [ ] http://commons.apache.org/collections/ with 1 occurrences migrated to:
  https://commons.apache.org/collections/ ([https](https://commons.apache.org/collections/) result 302).
* [ ] http://commons.apache.org/configuration/ with 1 occurrences migrated to:
  https://commons.apache.org/configuration/ ([https](https://commons.apache.org/configuration/) result 302).
* [ ] http://commons.apache.org/dbcp/ with 2 occurrences migrated to:
  https://commons.apache.org/dbcp/ ([https](https://commons.apache.org/dbcp/) result 302).
* [ ] http://commons.apache.org/digester/ with 1 occurrences migrated to:
  https://commons.apache.org/digester/ ([https](https://commons.apache.org/digester/) result 302).
* [ ] http://commons.apache.org/io/ with 3 occurrences migrated to:
  https://commons.apache.org/io/ ([https](https://commons.apache.org/io/) result 302).
* [ ] http://commons.apache.org/pool/ with 1 occurrences migrated to:
  https://commons.apache.org/pool/ ([https](https://commons.apache.org/pool/) result 302).
* [ ] http://dev.mysql.com/doc/connector-j/en/ with 1 occurrences migrated to:
  https://dev.mysql.com/doc/connector-j/en/ ([https](https://dev.mysql.com/doc/connector-j/en/) result 302).
* [ ] http://github.com/xetorthio/jedis/raw/master/LICENSE.txt with 1 occurrences migrated to:
  https://github.com/xetorthio/jedis/raw/master/LICENSE.txt ([https](https://github.com/xetorthio/jedis/raw/master/LICENSE.txt) result 302).
* [ ] http://maven.apache.org/ref/current/maven-core/lifecycles.html with 15 occurrences migrated to:
  https://maven.apache.org/ref/current/maven-core/lifecycles.html ([https](https://maven.apache.org/ref/current/maven-core/lifecycles.html) result 302).
* [ ] http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx with 2 occurrences migrated to:
  https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx ([https](https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx) result 302).
* [ ] http://repo.spring.io/libs-snapshot with 16 occurrences migrated to:
  https://repo.spring.io/libs-snapshot ([https](https://repo.spring.io/libs-snapshot) result 302).
* [ ] http://repo.spring.io/milestone with 31 occurrences migrated to:
  https://repo.spring.io/milestone ([https](https://repo.spring.io/milestone) result 302).
* [ ] http://repo.spring.io/release with 27 occurrences migrated to:
  https://repo.spring.io/release ([https](https://repo.spring.io/release) result 302).
* [ ] http://repo.spring.io/snapshot with 27 occurrences migrated to:
  https://repo.spring.io/snapshot ([https](https://repo.spring.io/snapshot) result 302).
* [ ] http://scripts.sil.org/OFL with 2 occurrences migrated to:
  https://scripts.sil.org/OFL ([https](https://scripts.sil.org/OFL) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-core with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-core ([https](https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-core) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-eureka-client with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-eureka-client ([https](https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-eureka-client) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-hystrix-stream with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-hystrix-stream ([https](https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-hystrix-stream) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-spectator with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-spectator ([https](https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-spectator) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-turbine-stream with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-turbine-stream ([https](https://spring.io/spring-cloud/spring-cloud-netflix/spring-cloud-netflix-turbine-stream) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream ([https](https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-codec with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-codec ([https](https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-codec) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-rxjava with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-rxjava ([https](https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-rxjava) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-test-support with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-test-support ([https](https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-test-support) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-test-support-internal with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-test-support-internal ([https](https://spring.io/spring-cloud/spring-cloud-stream-parent/spring-cloud-stream-test-support-internal) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-config with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-config ([https](https://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-config) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-core with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-core ([https](https://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-core) result 302).
* [ ] http://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-discovery with 1 occurrences migrated to:
  https://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-discovery ([https](https://spring.io/spring-cloud/spring-cloud-zookeeper/spring-cloud-zookeeper-discovery) result 302).
* [ ] http://spring.io/spring-security with 14 occurrences migrated to:
  https://spring.io/spring-security ([https](https://spring.io/spring-security) result 302).
* [ ] http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context with 1 occurrences migrated to:
  https://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context ([https](https://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context) result 302).
* [ ] http://www.eclipse.org/aether/aether-api/ with 4 occurrences migrated to:
  https://www.eclipse.org/aether/aether-api/ ([https](https://www.eclipse.org/aether/aether-api/) result 302).
* [ ] http://www.eclipse.org/aether/aether-connector-basic/ with 4 occurrences migrated to:
  https://www.eclipse.org/aether/aether-connector-basic/ ([https](https://www.eclipse.org/aether/aether-connector-basic/) result 302).
* [ ] http://www.eclipse.org/aether/aether-impl/ with 4 occurrences migrated to:
  https://www.eclipse.org/aether/aether-impl/ ([https](https://www.eclipse.org/aether/aether-impl/) result 302).
* [ ] http://www.eclipse.org/aether/aether-spi/ with 3 occurrences migrated to:
  https://www.eclipse.org/aether/aether-spi/ ([https](https://www.eclipse.org/aether/aether-spi/) result 302).
* [ ] http://www.eclipse.org/aether/aether-transport-file/ with 4 occurrences migrated to:
  https://www.eclipse.org/aether/aether-transport-file/ ([https](https://www.eclipse.org/aether/aether-transport-file/) result 302).
* [ ] http://www.eclipse.org/aether/aether-transport-http/ with 4 occurrences migrated to:
  https://www.eclipse.org/aether/aether-transport-http/ ([https](https://www.eclipse.org/aether/aether-transport-http/) result 302).
* [ ] http://www.eclipse.org/aether/aether-util/ with 3 occurrences migrated to:
  https://www.eclipse.org/aether/aether-util/ ([https](https://www.eclipse.org/aether/aether-util/) result 302).
* [ ] http://www.jboss.org/jbosstm/narayana-jta-all/jdbc/ with 1 occurrences migrated to:
  https://www.jboss.org/jbosstm/narayana-jta-all/jdbc/ ([https](https://www.jboss.org/jbosstm/narayana-jta-all/jdbc/) result 302).
* [ ] http://www.jboss.org/jbosstm/narayana-jta-all/jms/ with 1 occurrences migrated to:
  https://www.jboss.org/jbosstm/narayana-jta-all/jms/ ([https](https://www.jboss.org/jbosstm/narayana-jta-all/jms/) result 302).
* [ ] http://www.jboss.org/jbosstm/narayana-jta-all/jta/ with 1 occurrences migrated to:
  https://www.jboss.org/jbosstm/narayana-jta-all/jta/ ([https](https://www.jboss.org/jbosstm/narayana-jta-all/jta/) result 302).
* [ ] http://www.jboss.org/jbosstm/narayana-jts-all/narayana-jts-integration/ with 1 occurrences migrated to:
  https://www.jboss.org/jbosstm/narayana-jts-all/narayana-jts-integration/ ([https](https://www.jboss.org/jbosstm/narayana-jts-all/narayana-jts-integration/) result 302).

# Ignored
These URLs were intentionally ignored.

* http://accessors-smart/ with 3 occurrences
* http://docbook.org/ns/docbook with 48053 occurrences
* http://jacoco-maven-plugin with 5 occurrences
* http://link/to/your/nexus/or/artifactory/or/sth with 47 occurrences
* http://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl&gt with 3 occurrences
* http://loanIssuance/name with 16 occurrences
* http://localhost with 71 occurrences
* http://localhost:12345 with 16 occurrences
* http://localhost:12346 with 16 occurrences
* http://localhost:4000/spring-xyz/ with 1 occurrences
* http://localhost:8080/foo with 1 occurrences
* http://localhost:8081/artifactory/libs-release-local with 28 occurrences
* http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ with 14 occurrences
* http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar with 14 occurrences
* http://localhost:8085 with 19 occurrences
* http://localhost:8085</contractsRepositoryUrl&gt with 1 occurrences
* http://localhost:8888/users with 18 occurrences
* http://localhost:9876/api/books with 2 occurrences
* http://maven.apache.org/POM/4.0.0 with 56 occurrences
* http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 with 76 occurrences
* http://someNameThatShouldMapFraudDetectionServer/name with 16 occurrences
* http://some_url with 11 occurrences
* http://www with 1 occurrences
* http://www.springframework.org/schema/beans with 36 occurrences
* http://www.springframework.org/schema/integration with 36 occurrences
* http://www.w3.org/1999/xhtml with 160 occurrences
* http://www.w3.org/1999/xlink with 1 occurrences
* http://www.w3.org/2000/svg with 1 occurrences
* http://www.w3.org/2001/XMLSchema-instance with 84 occurrences
2019-03-26 03:21:34 -05:00

4155 lines
557 KiB
HTML

<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>2.&nbsp;Spring Cloud Contract Verifier</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="multi_spring-cloud-contract.html" title="Spring Cloud Contract"><link rel="up" href="multi_spring-cloud-contract.html" title="Spring Cloud Contract"><link rel="prev" href="multi__spring_cloud_contract.html" title="1.&nbsp;Spring Cloud Contract"><link rel="next" href="multi__spring_cloud_contract_wiremock.html" title="3.&nbsp;Spring Cloud Contract WireMock"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">2.&nbsp;Spring Cloud Contract Verifier</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__spring_cloud_contract.html">Prev</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_contract_wiremock.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_contract_verifier" href="#_spring_cloud_contract_verifier"></a>2.&nbsp;Spring Cloud Contract Verifier</h1></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_introduction" href="#_introduction"></a>2.1&nbsp;Introduction</h2></div></div></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>The Accurest project was initially started by Marcin Grzejszczak and Jakub Kubrynski (<a class="link" href="http://codearte.io" target="_top">codearte.io</a>)</p></td></tr></table></div><p>Just to make long story short - Spring Cloud Contract Verifier is a tool that enables Consumer Driven Contract (CDC) development of JVM-based applications. It is shipped
with <span class="emphasis"><em>Contract Definition Language</em></span> (DSL). Contract definitions are used to produce following resources:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">JSON stub definitions to be used by WireMock when doing integration testing on the client code (<span class="emphasis"><em>client tests</em></span>).
Test code must still be written by hand, test data is produced by Spring Cloud Contract Verifier.</li><li class="listitem">Messaging routes if you&#8217;re using one. We&#8217;re integrating with Spring Integration, Spring Cloud Stream, Spring AMQP and Apache Camel. You can however set your own integrations if you want to</li><li class="listitem">Acceptance tests (in JUnit or Spock) used to verify if server-side implementation of the API is compliant with the contract (<span class="emphasis"><em>server tests</em></span>).
Full test is generated by Spring Cloud Contract Verifier.</li></ul></div><p>Spring Cloud Contract Verifier moves TDD to the level of software architecture.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_why" href="#_why"></a>2.1.1&nbsp;Why?</h3></div></div></div><p>Let us assume that we have a system comprising of multiple microservices:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/2.0.x/docs/src/main/asciidoc/images/Deps.png" alt="Microservices Architecture"></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_testing_issues" href="#_testing_issues"></a>Testing issues</h4></div></div></div><p>If we wanted to test the application in top left corner if it can communicate with other services then we could do one of two things:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">deploy all microservices and perform end to end tests</li><li class="listitem">mock other microservices in unit / integration tests</li></ul></div><p>Both have their advantages but also a lot of disadvantages. Let&#8217;s focus on the latter.</p><p><span class="strong"><strong>Deploy all microservices and perform end to end tests</strong></span></p><p>Advantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">simulates production</li><li class="listitem">tests real communication between services</li></ul></div><p>Disadvantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">to test one microservice we would have to deploy 6 microservices, a couple of databases etc.</li><li class="listitem">the environment where the tests would be conducted would be locked for a single suite of tests (i.e. nobody else would be able to run the tests in the meantime).</li><li class="listitem">long to run</li><li class="listitem">very late feedback</li><li class="listitem">extremely hard to debug</li></ul></div><p><span class="strong"><strong>Mock other microservices in unit / integration tests</strong></span></p><p>Advantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">very fast feedback</li><li class="listitem">no infrastructure requirements</li></ul></div><p>Disadvantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">the implementor of the service creates stubs thus they might have nothing to do with the reality</li><li class="listitem">you can go to production with passing tests and failing production</li></ul></div><p>To solve the aforementioned issues Spring Cloud Contract Verifier with Stub Runner were created. Their main idea is to give you very fast feedback, without the need
to set up the whole world of microservices. If you work on stubs then the only applications you need are those that your application is using directly.</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/2.0.x/docs/src/main/asciidoc/images/Stubs2.png" alt="Stubbed Services"></div></div><p>Spring Cloud Contract Verifier gives you the certainty that the stubs that you&#8217;re using were created by the service that you&#8217;re calling. Also if you can use them it means that they were
tested against the producer&#8217;s side. In other words - you can trust those stubs.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_purposes" href="#_purposes"></a>2.1.2&nbsp;Purposes</h3></div></div></div><p>The main purposes of Spring Cloud Contract Verifier with Stub Runner are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">to ensure that WireMock / Messaging stubs (used when developing the client) are doing exactly what actual server-side implementation will do,</li><li class="listitem">to promote ATDD method and Microservices architectural style,</li><li class="listitem">to provide a way to publish changes in contracts that are immediately visible on both sides,</li><li class="listitem">to generate boilerplate test code used on the server side.</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Contract Verifier&#8217;s purpose is NOT to start writing business features in the contracts.
Let&#8217;s assume that we have a business use case of fraud check. If a user can be a fraud for 100 different reasons,
we would assume that you would create 2 contracts. One for the positive and one for the negative fraud case.
Contract tests are used to test contracts between applications and not to simulate full behaviour.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how" href="#_how"></a>2.1.3&nbsp;How</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_define_the_contract" href="#_define_the_contract"></a>Define the contract</h4></div></div></div><p>As consumers we need to define what exactly we want to achieve. We need to formulate our expectations. That&#8217;s why we write the following contract.</p><p>Let&#8217;s assume that we&#8217;d like to send the request containing the id of the client and the amount he wants to borrow from us. We&#8217;d like to send it to the /fraudcheck url via the PUT method.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts
org.springframework.cloud.contract.spec.Contract.make {
request { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (1)</span>
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (2)</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/fraudcheck'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (3)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (4)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"client.id"</span>: $(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{10}'</span>)),
loanAmount: <span class="hl-number">99999</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (5)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
response { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (6)</span>
status <span class="hl-number">200</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (7)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (8)</span>
fraudCheckStatus: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejection.reason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (9)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">/*
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `clientId` that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/json`
From the Producer perspective, in the autogenerated producer-side test:
(1) - A request will be sent to the producer
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/json.*`
*/</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_client_side" href="#_client_side"></a>Client Side</h4></div></div></div><p>Spring Cloud Contract will generate stubs, which you can use during client side testing.
You will have a WireMock instance / Messaging route up and running that simulates the service Y.
You would like to feed that instance with a proper stub definition.</p><p>At some point in time you need to send a request to the Fraud Detection service.</p><pre class="programlisting">ResponseEntity&lt;FraudServiceResponse&gt; response =
restTemplate.exchange(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span> + port + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>, HttpMethod.PUT,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpEntity&lt;&gt;(request, httpHeaders),
FraudServiceResponse.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);</pre><p>Annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment=WebEnvironment.NONE)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)</span></em>
<em><span class="hl-annotation" style="color: gray">@DirtiesContext</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {</pre><p>After that, during the tests Spring Cloud Contract will automatically find the stubs (simulating the real service) in Maven repository and expose them on configured (or random) port.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_server_side" href="#_server_side"></a>Server Side</h4></div></div></div><p>Being a service Y since you are developing your stub, you need to be sure that it&#8217;s actually resembling your
concrete implementation. You can&#8217;t have a situation where your stub acts in one way and your application on
production behaves in a different way.</p><p>That&#8217;s why from the provided stub acceptance tests will be generated that will ensure
that your application behaves in the same way as you define in your stub.</p><p>The autogenerated test would look like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"client.id\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1.json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['fraudCheckStatus']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[A-Z]{5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['rejection.reason']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_step_by_step_guide_to_cdc" href="#_step_by_step_guide_to_cdc"></a>2.1.4&nbsp;Step by step guide to CDC</h3></div></div></div><p>Let&#8217;s take an example of Fraud Detection and Loan Issuance process. The business scenario is such that we want to issue loans to people but don&#8217;t want them to steal the money from us. The current implementation of our system grants loans to everybody.</p><p>Let&#8217;s assume that the <code class="literal">Loan Issuance</code> is a client to the
<code class="literal">Fraud Detection</code> server. In the current sprint we are required to develop a new feature - if a client wants to borrow too much money then we mark him as fraud.</p><p>Technical remark - Fraud Detection will have artifact id <code class="literal">http-server</code>, Loan Issuance <code class="literal">http-client</code> and both have group id <code class="literal">com.example</code>.</p><p>Social remark - both client and server development teams need to communicate directly and discuss changes while
going through the process. CDC is all about communication.</p><p>The <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/2.0.x/samples/standalone/dsl/http-server" target="_top">server side code is available here</a> and <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/2.0.x/samples/standalone/dsl/http-client" target="_top">the client side code here</a>.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>In this case the ownership of the contracts lays on the producer side. It means that physically
all the contract are present in the producer&#8217;s repository</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_technical_note" href="#_technical_note"></a>Technical note</h4></div></div></div><p>If using the <span class="strong"><strong>SNAPSHOT</strong></span> / <span class="strong"><strong>Milestone</strong></span> / <span class="strong"><strong>Release Candidate</strong></span> versions please add the following section to your</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">repositories {
mavenCentral()
mavenLocal()
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/snapshot"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/milestone"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/release"</span> }
}</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_consumer_side_loan_issuance" href="#_consumer_side_loan_issuance"></a>Consumer side (Loan Issuance)</h4></div></div></div><p>As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):</p><p><span class="strong"><strong>start doing TDD by writing a test to your feature</strong></span></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> shouldBeRejectedDueToAbnormalLoanAmount() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
LoanApplication application = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoanApplication(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Client(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1234567890"</span>),
<span class="hl-number">99999</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
LoanApplicationResult loanApplication = service.loanApplication(application);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
assertThat(loanApplication.getRejectionReason()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre><p>We&#8217;ve just written a test of our new feature. If a loan application for a big amount is received we should reject that loan application with some description.</p><p><span class="strong"><strong>write the missing implementation</strong></span></p><p>At some point in time you need to send a request to the Fraud Detection service. Let&#8217;s assume that we&#8217;d like to send the request containing the id of the client and the amount he wants to borrow from us. We&#8217;d like to send it to the <code class="literal">/fraudcheck</code> url via the <code class="literal">PUT</code> method.</p><pre class="programlisting">ResponseEntity&lt;FraudServiceResponse&gt; response =
restTemplate.exchange(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span> + port + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>, HttpMethod.PUT,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpEntity&lt;&gt;(request, httpHeaders),
FraudServiceResponse.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);</pre><p>For simplicity we&#8217;ve hardcoded the port of the Fraud Detection service at <code class="literal">8080</code> and our application is running on <code class="literal">8090</code>.</p><p>If we&#8217;d start the written test it would obviously break since we have no service running on port <code class="literal">8080</code>.</p><p><span class="strong"><strong>clone the Fraud Detection service repository locally</strong></span></p><p>We&#8217;ll start playing around with the server side contract. That&#8217;s why we need to first clone it.</p><pre class="programlisting">git clone https://your-git-server.com/server-side.git local-http-server-repo</pre><p><span class="strong"><strong>define the contract locally in the repo of Fraud Detection service</strong></span></p><p>As consumers we need to define what exactly we want to achieve. We need to formulate our expectations. That&#8217;s why we write the following contract.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>We&#8217;re placing the contract under <code class="literal">src/test/resources/contracts/fraud</code> folder. The <code class="literal">fraud</code> folder
is important cause we&#8217;ll reference that folder in the producer&#8217;s test base class name.</p></td></tr></table></div><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts
org.springframework.cloud.contract.spec.Contract.make {
request { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (1)</span>
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (2)</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/fraudcheck'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (3)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (4)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"client.id"</span>: $(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{10}'</span>)),
loanAmount: <span class="hl-number">99999</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (5)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
response { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (6)</span>
status <span class="hl-number">200</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (7)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (8)</span>
fraudCheckStatus: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejection.reason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (9)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">/*
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `clientId` that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/json`
From the Producer perspective, in the autogenerated producer-side test:
(1) - A request will be sent to the producer
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/json.*`
*/</span></pre><p>The Contract is written using a statically typed Groovy DSL. You might be wondering what are those
<code class="literal">value(client(&#8230;&#8203;), server(&#8230;&#8203;))</code> parts. By using this notation Spring Cloud Contract allows you to
define parts of a JSON / URL / etc. which are dynamic. In case of an identifier or a timestamp you
don&#8217;t want to hardcode a value. You want to allow some different ranges of values. That&#8217;s why for
the consumer side you can set regular expressions matching those values. You can provide the body
either by means of a map notation or String with interpolations.
<a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_contract_dsl" target="_top">Consult the docs
for more information.</a> We highly recommend using the map notation!</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>It&#8217;s really important that you understand the map notation to set up contracts. Please read the
<a class="link" href="http://groovy-lang.org/json.html" target="_top">Groovy docs regarding JSON</a></p></td></tr></table></div><p>The aforementioned contract is an agreement between two sides that:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">if an HTTP request is sent with</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">a method <code class="literal">PUT</code> on an endpoint <code class="literal">/fraudcheck</code></li><li class="listitem">JSON body with <code class="literal">client.id</code> matching the regular expression <code class="literal">[0-9]{10}</code> and <code class="literal">loanAmount</code> equal to <code class="literal">99999</code></li><li class="listitem">and with a header <code class="literal">Content-Type</code> equal to <code class="literal">application/vnd.fraud.v1+json</code></li></ul></div></li><li class="listitem"><p class="simpara">then an HTTP response would be sent to the consumer that</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">has status <code class="literal">200</code></li><li class="listitem">contains JSON body with the <code class="literal">fraudCheckStatus</code> field containing a value <code class="literal">FRAUD</code> and the <code class="literal">rejectionReason</code> field having value <code class="literal">Amount too high</code></li><li class="listitem">and a <code class="literal">Content-Type</code> header with a value of <code class="literal">application/vnd.fraud.v1+json</code></li></ul></div></li></ul></div><p>Once we&#8217;re ready to check the API in practice in the integration tests we need to just install the stubs locally</p><p><span class="strong"><strong>add the Spring Cloud Contract Verifier plugin</strong></span></p><p>We can add either Maven or Gradle plugin - in this example we&#8217;ll show how to add Maven. First we need to add the <code class="literal">Spring Cloud Contract</code> BOM.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-dependencies.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p>Next, the <code class="literal">Spring Cloud Contract Verifier</code> Maven plugin</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Since the plugin was added we get the <code class="literal">Spring Cloud Contract Verifier</code> features which from the provided contracts:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">generate and run tests</li><li class="listitem">produce and install stubs</li></ul></div><p>We don&#8217;t want to generate tests since we, as consumers, want only to play with the stubs. That&#8217;s why we need to skip the tests generation and execution. When we execute:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">cd</span> local-http-server-repo
./mvnw clean install -DskipTests</pre><p>In the logs we&#8217;ll see something like this:</p><pre class="programlisting">[INFO] --- spring-cloud-contract-maven-plugin:<span class="hl-number">1.0</span>.<span class="hl-number">0.</span>BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
[INFO]
[INFO] --- maven-jar-plugin:<span class="hl-number">2.6</span>:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:<span class="hl-number">1.5</span>.<span class="hl-number">5.</span>BUILD-SNAPSHOT:repackage (default) @ http-server ---
[INFO]
[INFO] --- maven-install-plugin:<span class="hl-number">2.5</span>.<span class="hl-number">2</span>:install (default-install) @ http-server ---
[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar
[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar</pre><p>This line is extremely important</p><pre class="programlisting">[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar</pre><p>It&#8217;s confirming that the stubs of the <code class="literal">http-server</code> have been installed in the local repository.</p><p><span class="strong"><strong>run the integration tests</strong></span></p><p>In order to profit from the Spring Cloud Contract Stub Runner functionality of automatic stub downloading you have to do the following in our consumer side project (<code class="literal">Loan Application service</code>).</p><p>Add the <code class="literal">Spring Cloud Contract</code> BOM</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-dependencies.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p>Add the dependency to <code class="literal">Spring Cloud Contract Stub Runner</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>Annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators. Also provide the offline work switch since you&#8217;re playing with the collaborators offline (optional step).</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment=WebEnvironment.NONE)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)</span></em>
<em><span class="hl-annotation" style="color: gray">@DirtiesContext</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {</pre><p>Now if you run your tests you&#8217;ll see sth like this:</p><pre class="programlisting"><span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.403</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Desired version is + - will try to resolve the latest version
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.438</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved version is <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.439</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolving artifact com.example:http-server:jar:stubs:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT using remote repositories []
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.451</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved artifact com.example:http-server:jar:stubs:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.465</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar]
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.475</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacked file to [/var/folders/<span class="hl-number">0</span>p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">27.737</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT:stubs=<span class="hl-number">8080</span>}]</pre><p>Which means that Stub Runner has found your stubs and started a server for app with group id <code class="literal">com.example</code>, artifact id <code class="literal">http-server</code> with version <code class="literal">0.0.1-SNAPSHOT</code> of the stubs and with <code class="literal">stubs</code> classifier on port <code class="literal">8080</code>.</p><p><span class="strong"><strong>file a PR</strong></span></p><p>What we did until now is an iterative process. We can play around with the contract, install it locally and work on the consumer side until we&#8217;re happy with the contract.</p><p>Once we&#8217;re satisfied with the results and the test passes publish a PR to the server side. Currently the consumer side work is done.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_producer_side_fraud_detection_server" href="#_producer_side_fraud_detection_server"></a>Producer side (Fraud Detection server)</h4></div></div></div><p>As a developer of the Fraud Detection server (a server to the Loan Issuance service):</p><p><span class="strong"><strong>initial implementation</strong></span></p><p>As a reminder here you can see the initial implementation</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RequestMapping(value = "/fraudcheck", method = PUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> FraudCheckResult fraudCheck(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> FraudCheck fraudCheck) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}</pre><p><span class="strong"><strong>take over the PR</strong></span></p><pre class="programlisting">git checkout -b contract-change-pr master
git pull https://your-git-server.com/server-side-fork.git contract-change-pr</pre><p>You have to add the dependencies needed by the autogenerated tests</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>In the configuration of the Maven plugin we passed the <code class="literal">packageWithBaseClasses</code> property</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>We&#8217;ve decided to use the "convention based" naming by setting the <code class="literal">packageWithBaseClasses</code> property.
That means that 2 last packages will be combined into a name of the base test class. In our case the contracts
were placed under <code class="literal">src/test/resources/contracts/fraud</code>. Since we don&#8217;t have 2 packages starting from the <code class="literal">contracts</code>
folder we&#8217;re picking only one which is <code class="literal">fraud</code>. We&#8217;re adding the <code class="literal">Base</code> suffix and we&#8217;re capitalizing <code class="literal">fraud</code>.
That gives us the <code class="literal">FraudBase</code> test class name.</p></td></tr></table></div><p>That&#8217;s because all the generated tests will extend that class. Over there you can set up your Spring Context or
whatever is necessary. In our case we&#8217;re using <a class="link" href="http://rest-assured.io/" target="_top">Rest Assured MVC</a> to start the server side <code class="literal">FraudDetectionController</code>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example.fraud;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.module.mockmvc.RestAssuredMockMvc;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FraudBase {
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudDetectionController(),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudStatsController(stubbedStatsProvider()));
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> StatsProvider stubbedStatsProvider() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> fraudType -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">switch</span> (fraudType) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">case</span> DRUNKS:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">100</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">case</span> ALL:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">200</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">0</span>;
};
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> assertThatRejectionReasonIsNull(Object rejectionReason) {
assert rejectionReason == null;
}
}</pre><p>Now, if you run the <code class="literal">./mvnw clean install</code> you would get sth like this:</p><pre class="programlisting">Results :
Tests in error:
ContractVerifierTest.validate_shouldMarkClientAsFraud:<span class="hl-number">32</span> &raquo; IllegalState Parsed...</pre><p>That&#8217;s because you have a new contract from which a test was generated and it failed since you haven&#8217;t implemented the feature. The autogenerated test would look like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"client.id\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1.json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['fraudCheckStatus']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[A-Z]{5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['rejection.reason']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre><p>As you can see all the <code class="literal">producer()</code> parts of the Contract that were present in the <code class="literal">value(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code> blocks got injected into the test.</p><p>What&#8217;s important here to note is that on the producer side we also are doing TDD. We have expectations in form of a test. This test is shooting a request to our own application to an URL, headers and body defined in the contract. It also is expecting very precisely defined values in the response. In other words you have is your <code class="literal">red</code> part of <code class="literal">red</code>, <code class="literal">green</code> and <code class="literal">refactor</code>. Time to convert the <code class="literal">red</code> into the <code class="literal">green</code>.</p><p><span class="strong"><strong>write the missing implementation</strong></span></p><p>Now since we now what is the expected input and expected output let&#8217;s write the missing implementation.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RequestMapping(value = "/fraudcheck", method = PUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> FraudCheckResult fraudCheck(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> FraudCheck fraudCheck) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (amountGreaterThanThreshold(fraudCheck)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}</pre><p>If we execute <code class="literal">./mvnw clean install</code> again the tests will pass. Since the <code class="literal">Spring Cloud Contract Verifier</code> plugin adds the tests to the <code class="literal">generated-test-sources</code> you can actually run those tests from your IDE.</p><p><span class="strong"><strong>deploy your app</strong></span></p><p>Once you&#8217;ve finished your work it&#8217;s time to deploy your change. First merge the branch</p><pre class="programlisting">git checkout master
git merge --no-ff contract-change-pr
git push origin master</pre><p>Then we assume that your CI would run sth like <code class="literal">./mvnw clean deploy</code> which would publish both the application and the stub artifcats.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_consumer_side_loan_issuance_final_step" href="#_consumer_side_loan_issuance_final_step"></a>Consumer side (Loan Issuance) final step</h4></div></div></div><p>As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):</p><p><span class="strong"><strong>merge branch to master</strong></span></p><pre class="programlisting">git checkout master
git merge --no-ff contract-change-pr</pre><p><span class="strong"><strong>work online</strong></span></p><p>Now you can disable the offline work for Spring Cloud Contract Stub Runner and provide where the repository with your stubs is placed. At this moment the stubs of the server side will be automatically downloaded from Nexus / Artifactory.
You can switch off the value of the <code class="literal">workOffline</code> parameter in your annotation. Below you can see an
example of achieving the same by changing the properties.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ids</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example:http-server-dsl:+:stubs:8080'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositoryRoot</span>: https://repo.spring.io/libs-snapshot</pre><p>And that&#8217;s it!</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_dependencies" href="#_dependencies"></a>2.1.5&nbsp;Dependencies</h3></div></div></div><p>The best way to add the dependencies is to just use the proper <code class="literal">starter</code> dependency.</p><p>For <code class="literal">stub-runner</code> use <code class="literal">spring-cloud-starter-stub-runner</code> and when you&#8217;re using a plugin just add
<code class="literal">spring-cloud-starter-contract-verifier</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_additional_links" href="#_additional_links"></a>2.1.6&nbsp;Additional links</h3></div></div></div><p>Below you can find some resources related to Spring Cloud Contract Verifier and Stub Runner. Note that some can be outdated since the Spring Cloud Contract Verifier project
is under constant development.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_cloud_contract_video" href="#_spring_cloud_contract_video"></a>Spring Cloud Contract video</h4></div></div></div><p>You can check out the video from the Warsaw JUG about Spring Cloud Contract:</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_readings" href="#_readings"></a>Readings</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura" target="_top">Slides from Marcin Grzejszczak&#8217;s talk about Accurest</a></li><li class="listitem"><a class="link" href="http://toomuchcoding.com/blog/categories/accurest/" target="_top">Accurest related articles from Marcin Grzejszczak&#8217;s blog</a></li><li class="listitem"><a class="link" href="http://toomuchcoding.com/blog/categories/spring-cloud-contract/" target="_top">Spring Cloud Contract related articles from Marcin Grzejszczak&#8217;s blog</a></li><li class="listitem"><a class="link" href="http://groovy-lang.org/json.html" target="_top">Groovy docs regarding JSON</a></li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_samples" href="#_samples"></a>2.1.7&nbsp;Samples</h3></div></div></div><p>Here you can find some <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples" target="_top">samples</a>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_faq" href="#_faq"></a>2.2&nbsp;FAQ</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_why_use_spring_cloud_contract_verifier_and_not_x" href="#_why_use_spring_cloud_contract_verifier_and_not_x"></a>2.2.1&nbsp;Why use Spring Cloud Contract Verifier and not X ?</h3></div></div></div><p>For the time being Spring Cloud Contract Verifier is a JVM based tool. So it could be your first pick when you&#8217;re already creating
software for the JVM. This project has a lot of really interesting features but especially quite a few of them definitely make
Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Contract (CDC) tooling. Out of many the most interesting are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Possibility to do CDC with messaging</li><li class="listitem">Clear and easy to use, statically typed DSL</li><li class="listitem">Possibility to copy paste your current JSON file to the contract and only edit its elements</li><li class="listitem">Automatic generation of tests from the defined Contract</li><li class="listitem">Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory</li><li class="listitem">Spring Cloud integration - no discovery service is needed for integration tests</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_what_is_this_value_consumer_producer" href="#_what_is_this_value_consumer_producer"></a>2.2.2&nbsp;What is this value(consumer(), producer()) ?</h3></div></div></div><p>One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose.
What typically makes that difficult are the hard-coded values of request / response elements. For example dates or ids.
Imagine the following JSON request</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 20:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>and JSON response</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 21:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4231e1f-3ca9-48d3-b7e7-567d55f0d051"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>Imagine the pain required to set proper value of the <code class="literal">time</code> field (let&#8217;s assume that this content is generated by the
database) by changing the clock in the system or providing stub implementations of data providers. The same is related
to the field called <code class="literal">id</code>. Will you create a stubbed implementation of UUID generator? Makes little sense&#8230;&#8203;</p><p>So as a consumer you would like to send a request that matches any form of a time or any UUID. That way your system
will work as usual - will generate data and you won&#8217;t have to stub anything out. Let&#8217;s assume that in case of the aforementioned
JSON the most important part is the <code class="literal">body</code> field. You can focus on that and provide matching for other fields. In other words
you would like the stub to work like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES TIME"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES UUID"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>As far as the response goes as a consumer you need a concrete value that you can operate on. So such a JSON is valid</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 21:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4231e1f-3ca9-48d3-b7e7-567d55f0d051"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>As you could see in the previous sections we generate tests from contracts. So from the producer&#8217;s side the situation looks
much different. We&#8217;re parsing the provided contract and in the test we want to send a real request to your endpoints.
So for the case of a producer for the request we can&#8217;t have any sort of matching. We need concrete values that the
producer&#8217;s backend can work on. Such a JSON would be a valid one:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 20:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>On the other hand from the point of view of the validity of the contract the response doesn&#8217;t necessarily have to
contain concrete values of <code class="literal">time</code> or <code class="literal">id</code>. Let&#8217;s say that you generate those on the producer side - again, you&#8217;d
have to do a lot of stubbing to ensure that you always return the same values. That&#8217;s why from the producer&#8217;s side
what you might want is the following response:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES TIME"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES UUID"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>How can you then provide one time a matcher for the consumer and a concrete value for the producer and vice versa?
In Spring Cloud Contract we&#8217;re allowing you to provide a <span class="strong"><strong>dynamic value</strong></span>. That means that it can differ for both
sides of the communication. You can pass the values:</p><p>Either via the <code class="literal">value</code> method</p><pre class="programlisting">value(consumer(...), producer(...))
value(stub(...), test(...))
value(client(...), server(...))</pre><p>or using the <code class="literal">$()</code> method</p><pre class="programlisting">$(consumer(...), producer(...))
$(stub(...), test(...))
$(client(...), server(...))</pre><p>You can read more about this in the <a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_contract_dsl" target="_top">Contract DSL section</a>.</p><p>Calling <code class="literal">value()</code> or <code class="literal">$()</code> tells Spring Cloud Contract that you will be passing a dynamic value.
Inside the <code class="literal">consumer()</code> method you pass the value that should be used on the consumer side (in the generated stub).
Inside the <code class="literal">producer()</code> method you pass the value that should be used on the producer side (in the generated test).</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If on one side you have passed the regular expression and you haven&#8217;t passed the other, then the
other side will get auto-generated.</p></td></tr></table></div><p>Most often you will use that method together with the <code class="literal">regex</code> helper method. E.g. <code class="literal">consumer(regex('[0-9]{10}'))</code>.</p><p>To sum it up the contract for the aforementioned scenario would look more or less like this (the regular expression
for time and UUID are simplified and most likely invalid but we want to keep things very simple in this example):</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/someUrl'</span>
body([
time : value(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]-[0-5][0-9]-[0-5][0-9]'</span>)),
id: value(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9a-zA-z]{8}-[0-9a-zA-z]{4}-[0-9a-zA-z]{4}-[0-9a-zA-z]{12}'</span>))
body: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
])
}
response {
status <span class="hl-number">200</span>
body([
time : value(producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]-[0-5][0-9]-[0-5][0-9]'</span>)),
id: value([producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9a-zA-z]{8}-[0-9a-zA-z]{4}-[0-9a-zA-z]{4}-[0-9a-zA-z]{12}'</span>))
body: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
])
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Please read the <a class="link" href="http://groovy-lang.org/json.html" target="_top">Groovy docs related to JSON</a> to understand how to
properly structure the request / response bodies.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_to_do_stubs_versioning" href="#_how_to_do_stubs_versioning"></a>2.2.3&nbsp;How to do Stubs versioning?</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_api_versioning" href="#_api_versioning"></a>API Versioning</h4></div></div></div><p>Let&#8217;s try to answer a question what versioning really means. If you&#8217;re referring to the API version then there are
different approaches.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">use Hypermedia, links and do not version your API by any means</li><li class="listitem">pass versions through headers / urls</li></ul></div><p>I will not try to answer a question which approach is better. Whatever suit your needs and allows you to generate
business value should be picked.</p><p>Let&#8217;s assume that you do version your API. In that case you should provide as many contracts as many versions you support.
You can create a subfolder for every version or append it to th contract name - whatever suits you more.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_jar_versioning" href="#_jar_versioning"></a>JAR versioning</h4></div></div></div><p>If by versioning you mean the version of the JAR that contains the stubs then there are essentially two main approaches.</p><p>Let&#8217;s assume that you&#8217;re doing Continuous Delivery / Deployment which means that you&#8217;re generating a new version of
the jar each time you go through the pipeline and that jar can go to production at any time. For example your jar version
looks like this (it got built on the 20.10.2016 at 20:15:21) :</p><pre class="programlisting"><span class="hl-number">1.0</span>.<span class="hl-number">0.20161020</span>-<span class="hl-number">201521</span>-RELEASE</pre><p>In that case your generated stub jar will look like this.</p><pre class="programlisting"><span class="hl-number">1.0</span>.<span class="hl-number">0.20161020</span>-<span class="hl-number">201521</span>-RELEASE-stubs.jar</pre><p>In this case you should inside your <code class="literal">application.yml</code> or <code class="literal">@AutoConfigureStubRunner</code> when referencing stubs provide the
latest version of the stubs. You can do that by passing the <code class="literal">+</code> sign. Example</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:+:stubs:8080"</span>})</pre><p>If the versioning however is fixed (e.g. <code class="literal">1.0.4.RELEASE</code> or <code class="literal">2.1.1</code>) then you have to set the concrete value of the jar
version. Example for 2.1.1.</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:2.1.1:stubs:8080"</span>})</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_dev_or_prod_stubs" href="#_dev_or_prod_stubs"></a>Dev or prod stubs</h4></div></div></div><p>You can manipulate the classifier to run the tests against current development version of the stubs of other services
or the ones that were deployed to production. If you alter your build to deploy the stubs with the <code class="literal">prod-stubs</code> classifier
once you reach production deployment then you can run tests in one case with dev stubs and one with prod stubs.</p><p>Example of tests using development version of stubs</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:+:stubs:8080"</span>})</pre><p>Example of tests using production version of stubs</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:+:prod-stubs:8080"</span>})</pre><p>You can pass those values also via properties from your deployment pipeline.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_repo_with_contracts" href="#_common_repo_with_contracts"></a>2.2.4&nbsp;Common repo with contracts</h3></div></div></div><p>Another way of storing contracts other than having them with the producer is keeping them in a common place.
It can be related to security issues where the consumers can&#8217;t clone the producer&#8217;s code. Also if you keep
contracts in a single place then you, as a producer, will know how many consumers you have and which
consumer will you break with your local changes.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_repo_structure" href="#_repo_structure"></a>Repo structure</h4></div></div></div><p>Let&#8217;s assume that we have a producer with coordinates <code class="literal">com.example:server</code> and 3 consumers: <code class="literal">client1</code>,
<code class="literal">client2</code>, <code class="literal">client3</code>. Then in the repository with common contracts you would have the following setup
(which you can checkout <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/2.0.x/samples/standalone/contracts" target="_top">here</a>:</p><pre class="programlisting">&#9500;&#9472;&#9472; com
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; example
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; server
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; client1
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; expectation.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; client2
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; expectation.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; client3
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; expectation.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; pom.xml
&#9500;&#9472;&#9472; mvnw
&#9500;&#9472;&#9472; mvnw.cmd
&#9500;&#9472;&#9472; pom.xml
&#9492;&#9472;&#9472; src
&#9492;&#9472;&#9472; assembly
&#9492;&#9472;&#9472; contracts.xml</pre><p>As you can see the under the slash-delimited groupid <code class="literal">/</code> artifact id folder (<code class="literal">com/example/server</code>) you have
expectations of the 3 consumers (<code class="literal">client1</code>, <code class="literal">client2</code> and <code class="literal">client3</code>). Expectations are the standard Groovy DSL
contract files as described throughout this documentation. This repository has to produce a JAR file that maps
one to one to the contents of the repo.</p><p>Example of a <code class="literal">pom.xml</code> inside the <code class="literal">server</code> folder.</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;modelVersion&gt;</span>4.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/modelVersion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>server<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>0.0.1-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Server Stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;description&gt;</span>POM used to install locally stubs for consumer side<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/description&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-parent<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.0.0.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;relativePath /&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project.build.sourceEncoding&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;java.version&gt;</span>1.8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/java.version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring-cloud-contract.version&gt;</span>2.0.0.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring-cloud-contract.version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring-cloud-dependencies.version&gt;</span>Finchley.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring-cloud-dependencies.version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;excludeBuildFolders&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/excludeBuildFolders&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-dependencies.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- By default it would search under src/test/resources/ --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsDirectory&gt;</span>${project.basedir}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project&gt;</span></pre><p>As you can see there are no dependencies other than the Spring Cloud Contract Maven Plugin.
Those poms are necessary for the consumer side to run <code class="literal">mvn clean install -DskipTests</code> to locally install
stubs of the producer project.</p><p>The <code class="literal">pom.xml</code> in the root folder can look like this:</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;modelVersion&gt;</span>4.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/modelVersion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example.standalone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>0.0.1-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;description&gt;</span>Contains all the Spring Cloud Contracts, well, contracts. JAR used by the producers to generate tests and stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/description&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project.build.sourceEncoding&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.maven.plugins<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>maven-assembly-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>prepare-package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>single<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;attach&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/attach&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;descriptor&gt;</span>${basedir}/src/assembly/contracts.xml<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/descriptor&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- If you want an explicit classifier remove the following line --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appendAssemblyId&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appendAssemblyId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project&gt;</span></pre><p>It&#8217;s using the assembly plugin in order to build the JAR with all the contracts. Example of such setup is here:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;assembly</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 https://maven.apache.org/xsd/assembly-1.1.3.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>project<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;format&gt;</span>jar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/format&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includeBaseDirectory&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includeBaseDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.basedir}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;useDefaultExcludes&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/useDefaultExcludes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;excludes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>**/${project.build.directory}/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>mvnw<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>mvnw.cmd<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>.mvn/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>src/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/excludes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/assembly&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_workflow" href="#_workflow"></a>Workflow</h4></div></div></div><p>The workflow would look similar to the one presented in the <code class="literal">Step by step guide to CDC</code>. The only difference
is that the producer doesn&#8217;t own the contracts anymore. So the consumer and the producer have to work on
common contracts in a common repository.</p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_consumer" href="#_consumer"></a>Consumer</h5></div></div></div><p>When the <span class="strong"><strong>consumer</strong></span> wants to work on the contracts offline, instead of cloning the producer code, the
consumer team clones the common repository, goes to the required producer&#8217;s folder (e.g. <code class="literal">com/example/server</code>)
and runs <code class="literal">mvn clean install -DskipTests</code> to install locally the stubs converted from the contracts.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You need to have <a class="link" href="https://maven.apache.org/download.cgi" target="_top">Maven installed locally</a></p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_producer" href="#_producer"></a>Producer</h5></div></div></div><p>As a <span class="strong"><strong>producer</strong></span> it&#8217;s enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency
of the JAR containing the contracts:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsRepositoryUrl&gt;</span>http://link/to/your/nexus/or/artifactory/or/sth<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsRepositoryUrl&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example.standalone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>With this setup the JAR with groupid <code class="literal">com.example.standalone</code> and artifactid <code class="literal">contracts</code> will be downloaded
from <code class="literal"><a class="link" href="http://link/to/your/nexus/or/artifactory/or/sth" target="_top">http://link/to/your/nexus/or/artifactory/or/sth</a></code>. It will be then unpacked in a local temporary folder
and contracts present under the <code class="literal">com/example/server</code> will be picked as the ones used to generate the
tests and the stubs. Due to this convention the producer team will know which consumer teams will be broken
when some incompatible changes are done.</p><p>The rest of the flow looks the same.</p></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_can_i_have_multiple_base_classes_for_tests" href="#_can_i_have_multiple_base_classes_for_tests"></a>2.2.5&nbsp;Can I have multiple base classes for tests?</h3></div></div></div><p>Yes! Check out the <a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_different_base_classes_for_contracts" target="_top">Different base classes for contracts</a> sections
of either Gradle or Maven plugins.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_can_i_debug_the_request_response_being_sent_by_the_generated_tests_client" href="#_how_can_i_debug_the_request_response_being_sent_by_the_generated_tests_client"></a>2.2.6&nbsp;How can I debug the request/response being sent by the generated tests client?</h3></div></div></div><p>The generated tests all boil down to RestAssured in some form or fashion which relies on <a class="link" href="https://hc.apache.org/httpcomponents-client-ga/" target="_top">Apache HttpClient</a>. HttpClient has a facility called <a class="link" href="https://hc.apache.org/httpcomponents-client-ga/logging.html#Wire_Logging" target="_top">wire logging</a> which logs the entire request and response to HttpClient. Spring Boot has a logging <a class="link" href="https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html" target="_top">common application property</a> for doing this sort of thing, just add this to your application properties</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">logging.level.org.apache.http.wire</span>=DEBUG</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_can_i_debug_the_mapping_request_response_being_sent_by_wiremock" href="#_how_can_i_debug_the_mapping_request_response_being_sent_by_wiremock"></a>2.2.7&nbsp;How can I debug the mapping/request/response being sent by WireMock?</h3></div></div></div><p>Starting from version <code class="literal">1.2.0</code> we turn on WireMock logging to
info and the WireMock notifier to being verbose. Now you will
exactly know what request was received by WireMock server and which
matching response definition was picked.</p><p>To turn off this feature just bump WireMock logging to <code class="literal">ERROR</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">logging.level.com.github.tomakehurst.wiremock</span>=ERROR</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_can_i_see_what_got_registered_in_the_http_server_stub" href="#_how_can_i_see_what_got_registered_in_the_http_server_stub"></a>2.2.8&nbsp;How can I see what got registered in the HTTP server stub?</h3></div></div></div><p>You can use the <code class="literal">mappingsOutputFolder</code> property on <code class="literal">@AutoConfigureStubRunner</code> or <code class="literal">StubRunnerRule</code>
to dump all mappings per artifact id. Also the port at which the given stub server was
started will be attached.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_can_i_reference_the_request_from_the_response" href="#_can_i_reference_the_request_from_the_response"></a>2.2.9&nbsp;Can I reference the request from the response?</h3></div></div></div><p>Yes! With version 1.1.0 we&#8217;ve added such a possibility. On the HTTP stub server side we&#8217;re providing support
for this for WireMock. In case of other HTTP server stubs you&#8217;ll have to implement the approach yourself.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_can_i_reference_text_from_file" href="#_can_i_reference_text_from_file"></a>2.2.10&nbsp;Can I reference text from file?</h3></div></div></div><p>Yes! With version 1.2.0 we&#8217;ve added such a possibility. It&#8217;s enough to call <code class="literal">file(&#8230;&#8203;)</code> method in the
DSL and provide a path relative to where the contract lays.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spring_cloud_contract_verifier_http" href="#_spring_cloud_contract_verifier_http"></a>2.3&nbsp;Spring Cloud Contract Verifier HTTP</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_gradle_project" href="#_gradle_project"></a>2.3.1&nbsp;Gradle Project</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_prerequisites" href="#_prerequisites"></a>Prerequisites</h4></div></div></div><p>In order to use Spring Cloud Contract Verifier with WireMock you have to use Gradle or Maven plugin.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>If you want to use Spock in your projects you have to add separately
the <code class="literal">spock-core</code> and <code class="literal">spock-spring</code> modules. Check <a class="link" href="https://spockframework.github.io/" target="_top">Spock docs for more information</a></p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_add_gradle_plugin_with_dependencies" href="#_add_gradle_plugin_with_dependencies"></a>Add gradle plugin with dependencies</h5></div></div></div><pre class="programlisting">buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}"</span>
}
}
apply plugin: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'groovy'</span>
apply plugin: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'spring-cloud-contract'</span>
dependencyManagement {
imports {
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-dependencies:${verifier_version}"</span>
}
}
dependencies {
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.codehaus.groovy:groovy-all:2.4.6'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// example with adding Spock core and Spock Spring</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.spockframework:spock-core:1.0-groovy-2.4'</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.spockframework:spock-spring:1.0-groovy-2.4'</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud:spring-cloud-starter-contract-verifier'</span>
}</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_gradle_and_rest_assured_2_0" href="#_gradle_and_rest_assured_2_0"></a>Gradle and Rest Assured 2.0</h5></div></div></div><p>By default Rest Assured 3.x is added to the classpath. However in order to give the users the
opportunity to use Rest Assured 2.x it&#8217;s enough to add it to the plugins classpath.</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:rest-assured:2.5.0"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:spring-mock-mvc:2.5.0"</span>
}
}
depenendencies {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// all dependencies</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you can exclude rest-assured from spring-cloud-contract-verifier</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:rest-assured:2.5.0"</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:spring-mock-mvc:2.5.0"</span>
}</pre><p>That way the plugin will automatically see that Rest Assured 2.x is present on the classpath
and will modify the imports accordingly.</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_snapshot_versions_for_gradle" href="#_snapshot_versions_for_gradle"></a>Snapshot versions for Gradle</h5></div></div></div><p>Add the additional snapshot repository to your build.gradle to use snapshot versions which are automatically uploaded after every successful build:</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/snapshot"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/milestone"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/release"</span> }
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_add_stubs" href="#_add_stubs"></a>Add stubs</h5></div></div></div><p>By default Spring Cloud Contract Verifier is looking for stubs in <code class="literal">src/test/resources/contracts</code> directory.</p><p>Directory containing stub definitions is treated as a class name, and each stub definition is treated as a single test.
We assume that it contains at least one directory which will be used as test class name. If there is more than one level of nested directories all except the last one will be used as package name.
So with following structure</p><pre class="programlisting">src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy</pre><p>Spring Cloud Contract Verifier will create test class <code class="literal">defaultBasePackage.MyService</code> with two methods</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">shouldCreateUser()</code></li><li class="listitem"><code class="literal">shouldReturnUser()</code></li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_run_plugin" href="#_run_plugin"></a>Run plugin</h4></div></div></div><p>Plugin registers itself to be invoked before <code class="literal">check</code> task. You have nothing to do as long as you want it to be part of your build process. If you just want to generate tests please invoke <code class="literal">generateContractTests</code> task.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_default_setup" href="#_default_setup"></a>Default setup</h4></div></div></div><p>Default Gradle Plugin setup creates the following Gradle part of the build (it&#8217;s a pseudocode)</p><pre class="programlisting">contracts {
targetFramework = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'JUNIT'</span>
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'MockMvc'</span>
generatedTestSourcesDir = project.file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/generated-test-sources/contracts"</span>)
contractsDslDir = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.rootDir}/src/test/resources/contracts"</span>
basePackageForTests = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.verifier.tests'</span>
stubsOutputDir = project.file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/stubs"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the following properties are used when you want to provide where the JAR with contract lays</span>
contractDependency {
stringNotation = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>
}
contractsPath = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>
contractsWorkOffline = false
contractRepository {
cacheDownloadedContracts(true)
}
}
tasks.create(type: Jar, name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'verifierStubsJar'</span>, dependsOn: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'generateClientStubs'</span>) {
baseName = project.name
classifier = contracts.stubsSuffix
from contractVerifier.stubsOutputDir
}
project.artifacts {
archives task
}
tasks.create(type: Copy, name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'copyContracts'</span>) {
from contracts.contractsDslDir
into contracts.stubsOutputDir
}
verifierStubsJar.dependsOn <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'copyContracts'</span>
publishing {
publications {
stubs(MavenPublication) {
artifactId project.name
artifact verifierStubsJar
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_configure_plugin" href="#_configure_plugin"></a>Configure plugin</h4></div></div></div><p>To change default configuration just add <code class="literal">contracts</code> snippet to your Gradle config</p><pre class="programlisting">contracts {
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'MockMvc'</span>
baseClassForTests = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.mycompany.tests'</span>
generatedTestSourcesDir = project.file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'src/generatedContract'</span>)
}</pre><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_configuration_options" href="#_configuration_options"></a>Configuration options</h5></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>testMode</strong></span> - defines mode for acceptance tests. By default MockMvc which is based on Spring&#8217;s MockMvc. It can also be changed to <span class="strong"><strong>JaxRsClient</strong></span> or to <span class="strong"><strong>Explicit</strong></span> for real HTTP calls.</li><li class="listitem"><span class="strong"><strong>imports</strong></span> - array with imports that should be included in generated tests (for example ['org.myorg.Matchers']). By default empty array []</li><li class="listitem"><span class="strong"><strong>staticImports</strong></span> - array with static imports that should be included in generated tests(for example ['org.myorg.Matchers.*']). By default empty array []</li><li class="listitem"><span class="strong"><strong>basePackageForTests</strong></span> - specifies base package for all generated tests. If not set the value will be picked from <code class="literal">baseClassForTests&#8217;s package and from
`packageWithBaseClasses</code>. If neither of these are set then the value will be set to <code class="literal">org.springframework.cloud.contract.verifier.tests</code></li><li class="listitem"><span class="strong"><strong>baseClassForTests</strong></span> - base class for all generated tests. By default <code class="literal">spock.lang.Specification</code> if using Spock tests.</li><li class="listitem"><span class="strong"><strong>packageWithBaseClasses</strong></span> - instead of providing a fixed value for base class you can provide a package where all the base classes lay. Takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>.</li><li class="listitem"><span class="strong"><strong>baseClassMappings</strong></span> - explicitly map contract package to a FQN of a base class. Takes precedence over <span class="strong"><strong>packageWithBaseClasses</strong></span> and <span class="strong"><strong>baseClassForTests</strong></span>.</li><li class="listitem"><span class="strong"><strong>ruleClassForTests</strong></span> - specifies Rule which should be added to generated test classes.</li><li class="listitem"><span class="strong"><strong>ignoredFiles</strong></span> - Ant matcher allowing defining stub files for which processing should be skipped. By default empty array []</li><li class="listitem"><span class="strong"><strong>contractsDslDir</strong></span> - directory containing contracts written using the GroovyDSL. By default <code class="literal">$rootDir/src/test/resources/contracts</code></li><li class="listitem"><span class="strong"><strong>generatedTestSourcesDir</strong></span> - test source directory where tests generated from Groovy DSL should be placed. By default <code class="literal">$buildDir/generated-test-sources/contractVerifier</code></li><li class="listitem"><span class="strong"><strong>stubsOutputDir</strong></span> - dir where the generated WireMock stubs from Groovy DSL should be placed</li><li class="listitem"><span class="strong"><strong>targetFramework</strong></span> - the target test framework to be used; currently Spock and JUnit are supported with JUnit being the default framework</li></ul></div><p>The following properties are used when you want to provide where the JAR with contract lays</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>contractDependency</strong></span> - the Dependency that provides <code class="literal">groupid:artifactid:version:classifier</code> coordinates. You can use the <code class="literal">contractDependency</code> closure to set it up</li><li class="listitem"><span class="strong"><strong>contractsPath</strong></span> - if contract deps are downloaded will default to <code class="literal">groupid/artifactid</code> where <code class="literal">groupid</code> will be slash separated. Otherwise will scan contracts under provided directory</li><li class="listitem"><span class="strong"><strong>contractsWorkOffline</strong></span> - in order not to download the dependencies each time you can download them once and work offline afterwards (reuse local Maven repo)</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_single_base_class_for_all_tests" href="#_single_base_class_for_all_tests"></a>Single base class for all tests</h5></div></div></div><p>When using Spring Cloud Contract Verifier in default MockMvc you need to create a base specification for all generated acceptance tests. In this class you need to point to endpoint which should be verified.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BaseMockMvcSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PairIdController())
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isProperCorrelationId(Integer correlationId) {
assert correlationId == <span class="hl-number">123456</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isEmpty(String value) {
assert value == null
}
}</pre><p>In case of using <code class="literal">Explicit</code> mode, you can use base class to initialize the whole tested app similarly as in regular integration tests. In case of <code class="literal">JAXRSCLIENT</code> mode this base class
should also contain <code class="literal">protected WebTarget webTarget</code> field, right now the only option to test JAX-RS API is to start a web server.</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_different_base_classes_for_contracts" href="#_different_base_classes_for_contracts"></a>Different base classes for contracts</h5></div></div></div><p>If your base classes differ between contracts you can tell the Spring Cloud Contract plugin which class should get
extended by the autogenerated tests. You have two options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">follow a convention by providing the <code class="literal">packageWithBaseClasses</code></li><li class="listitem">provide explicit mapping via <code class="literal">baseClassMappings</code></li></ul></div><p><span class="strong"><strong>Convention</strong></span></p><p>The convention is such that if you have a contract under e.g. <code class="literal">src/test/resources/contract/foo/bar/baz/</code> and provide the value of the <code class="literal">packageWithBaseClasses</code> property
to <code class="literal">com.example.base</code> then we will assume that there is a <code class="literal">BarBazBase</code> class under <code class="literal">com.example.base</code> package. In other words we take last two parts of package
if they exist and form a class with a <code class="literal">Base</code> suffix. Takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>. Example of usage in the <code class="literal">contracts</code> closure:</p><pre class="programlisting">packageWithBaseClasses = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example.base'</span></pre><p><span class="strong"><strong>Mapping</strong></span></p><p>You can manually map a regular expression of the contract&#8217;s package to fully qualified name of the base class for the matched contract.
Let&#8217;s take a look at the following example:</p><pre class="programlisting">baseClassForTests = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example.FooBase"</span>
baseClassMappings {
baseClassMapping(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.*/com/.*'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example.ComBase'</span>)
baseClassMapping(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.*/bar/.*'</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example.BarBase'</span>)
}</pre><p>Let&#8217;s assume that you have contracts under
- <code class="literal">src/test/resources/contract/com/</code>
- <code class="literal">src/test/resources/contract/foo/</code></p><p>By providing the <code class="literal">baseClassForTests</code> we have a fallback in case mapping didn&#8217;t succeed (you could also provide
the <code class="literal">packageWithBaseClasses</code> as fallback). That way the tests generated from <code class="literal">src/test/resources/contract/com/</code> contracts
will be extending the <code class="literal">com.example.ComBase</code> whereas the rest of tests will extend <code class="literal">com.example.FooBase</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_invoking_generated_tests" href="#_invoking_generated_tests"></a>Invoking generated tests</h4></div></div></div><p>To ensure that provider side is complaint with defined contracts, you need to invoke:</p><pre class="programlisting">./gradlew generateContractTests <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_cloud_contract_verifier_on_consumer_side" href="#_spring_cloud_contract_verifier_on_consumer_side"></a>Spring Cloud Contract Verifier on consumer side</h4></div></div></div><p>In consumer service you need to configure Spring Cloud Contract Verifier plugin in exactly the same way as in case of provider. If you don&#8217;t want to use Stub Runner then you need to copy contracts stored in
<code class="literal">src/test/resources/contracts</code> and generate WireMock json stubs using:</p><pre class="programlisting">./gradlew generateClientStubs</pre><p>Note that <code class="literal">stubsOutputDir</code> option has to be set for stub generation to work.</p><p>When present, json stubs can be used in consumer automated tests.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
<em><span class="hl-annotation" style="color: gray">@ClassRule</span></em>
<em><span class="hl-annotation" style="color: gray">@Shared</span></em>
WireMockClassRule wireMockRule == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockClassRule()
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
LoanApplicationService sut
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should successfully apply for loan'</span>() {
given:
LoanApplication application =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoanApplication(client: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Client(clientPesel: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'12345678901'</span>), amount: <span class="hl-number">123.123</span>)
when:
LoanApplicationResult loanApplication == sut.loanApplication(application)
then:
loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED
loanApplication.rejectionReason == null
}
}</pre><p>Underneath LoanApplication makes a call to FraudDetection service. This request is handled by WireMock server configured using stubs generated by Spring Cloud Contract Verifier.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_in_your_maven_project" href="#_using_in_your_maven_project"></a>2.3.2&nbsp;Using in your Maven project</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_add_maven_plugin" href="#_add_maven_plugin"></a>Add maven plugin</h4></div></div></div><p>Add the Spring Cloud Contract BOM</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-dependencies.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p>Next, the <code class="literal">Spring Cloud Contract Verifier</code> Maven plugin</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>You can read more in the <a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/" target="_top">Spring Cloud Contract Maven Plugin Docs</a></p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_maven_and_rest_assured_2_0" href="#_maven_and_rest_assured_2_0"></a>Maven and Rest Assured 2.0</h5></div></div></div><p>By default Rest Assured 3.x is added to the classpath. However in order to give the users the
opportunity to use Rest Assured 2.x it&#8217;s enough to add it to the plugins classpath.</p><pre class="programlisting">&lt;plugin&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-maven-plugin&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;extensions&gt;true&lt;/extensions&gt;
&lt;configuration&gt;
&lt;packageWithBaseClasses&gt;com.example&lt;/packageWithBaseClasses&gt;
&lt;/configuration&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-verifier&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;rest-assured&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;compile&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;spring-mock-mvc&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;compile&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/plugin&gt;
&lt;dependencies&gt;
&lt;!-- all dependencies --&gt;
&lt;!-- you can exclude rest-assured from spring-cloud-contract-verifier --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;rest-assured&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;spring-mock-mvc&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;</pre><p>That way the plugin will automatically see that Rest Assured 3.x is present on the classpath
and will modify the imports accordingly.</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_snapshot_versions_for_maven" href="#_snapshot_versions_for_maven"></a>Snapshot versions for Maven</h5></div></div></div><p>For Snapshot / Milestone versions you have to add the following section to your <code class="literal">pom.xml</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span></pre></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_add_stubs_2" href="#_add_stubs_2"></a>Add stubs</h4></div></div></div><p>By default Spring Cloud Contract Verifier is looking for stubs in <code class="literal">src/test/resources/contracts</code> directory.
Directory containing stub definitions is treated as a class name, and each stub definition is treated as a single test.
We assume that it contains at least one directory which will be used as test class name. If there is more than one level of nested directories all except the last one will be used as package name.
So with following structure</p><pre class="programlisting">src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy</pre><p>Spring Cloud Contract Verifier will create test class <code class="literal">defaultBasePackage.MyService</code> with two methods
- <code class="literal">shouldCreateUser()</code>
- <code class="literal">shouldReturnUser()</code></p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_run_plugin_2" href="#_run_plugin_2"></a>Run plugin</h4></div></div></div><p>Plugin goal <code class="literal">generateTests</code> is assigned to be invoked in phase <code class="literal">generate-test-sources</code>. You have nothing to do as long as you want it to be part of your build process. If you just want to generate tests please invoke <code class="literal">generateTests</code> goal.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_configure_plugin_2" href="#_configure_plugin_2"></a>Configure plugin</h4></div></div></div><p>To change default configuration just add <code class="literal">configuration</code> section to plugin definition or <code class="literal">execution</code> definition.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>convert<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>generateStubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>generateTests<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;basePackageForTests&gt;</span>org.springframework.cloud.verifier.twitter.place<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/basePackageForTests&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassForTests&gt;</span>org.springframework.cloud.verifier.twitter.place.BaseMockMvcSpec<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassForTests&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_important_configuration_options" href="#_important_configuration_options"></a>Important configuration options</h5></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>testMode</strong></span> - defines mode for acceptance tests. By default <code class="literal">MockMvc</code> which is based on Spring&#8217;s MockMvc. It can also be changed to <code class="literal">JaxRsClient</code> or to <code class="literal">Explicit</code> for real HTTP calls.</li><li class="listitem"><span class="strong"><strong>basePackageForTests</strong></span> - specifies base package for all generated tests. If not set the value will be picked from <code class="literal">baseClassForTests&#8217;s package and from
`packageWithBaseClasses</code>. If neither of these are set then the value will be set to <code class="literal">org.springframework.cloud.contract.verifier.tests</code></li><li class="listitem"><span class="strong"><strong>ruleClassForTests</strong></span> - specifies Rule which should be added to generated test classes.</li><li class="listitem"><span class="strong"><strong>baseClassForTests</strong></span> - base class for generated tests. By default <code class="literal">spock.lang.Specification</code> if using Spock tests.</li><li class="listitem"><span class="strong"><strong>contractsDirectory</strong></span> - directory containing contracts written using the GroovyDSL. By default <code class="literal">/src/test/resources/contracts</code>.</li><li class="listitem"><span class="strong"><strong>testFramework</strong></span> - the target test framework to be used; currently Spock and JUnit are supported with JUnit being the default framework</li><li class="listitem"><span class="strong"><strong>packageWithBaseClasses</strong></span> - instead of providing a fixed value for base class you can provide a package where all the base classes lay.
The convention is such that if you have a contract under <code class="literal">src/test/resources/contract/foo/bar/baz/</code> and provide the value of this property
to <code class="literal">com.example.base</code> then we will assume that there is a <code class="literal">BarBazBase</code> class under <code class="literal">com.example.base</code> package. Takes precedence
over <span class="strong"><strong>baseClassForTests</strong></span></li><li class="listitem"><span class="strong"><strong>baseClassMappings</strong></span> - list of base class mappings that where you have to provide <code class="literal">contractPackageRegex</code> which is checked
against the package in which the contract lays and <code class="literal">baseClassFQN</code> that maps to fully qualified name of the base class for the matched
contract. If you have a contract under <code class="literal">src/test/resources/contract/foo/bar/baz/</code> and map the property <code class="literal">.*</code> &#8594; <code class="literal">com.example.base.BaseClass</code> then
the test class generated from these contracts will extend <code class="literal">com.example.base.BaseClass</code>. Takes precedence over <span class="strong"><strong>packageWithBaseClasses</strong></span>
and <span class="strong"><strong>baseClassForTests</strong></span>.</li></ul></div><p>If you want to download your contract definitions from a Maven repository you can use</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>contractDependency</strong></span> - the contract dependency that contains all the packaged contracts</li><li class="listitem"><span class="strong"><strong>contractsPath</strong></span> - path to concrete contracts in the JAR with packaged contracts. Defaults to <code class="literal">groupid/artifactid</code> where <code class="literal">gropuid</code> is slash separated.</li><li class="listitem"><span class="strong"><strong>contractsWorkOffline</strong></span> - if the dependencies should be downloaded or local Maven only should be reused</li><li class="listitem"><span class="strong"><strong>contractsRepositoryUrl</strong></span> - <span class="strong"><strong>DEPRECATED PROPERTY - please use the <code class="literal">contractRepository</code> closure</strong></span> - URL to a repo with the artifacts with contracts, if not provided should use the current Maven ones</li><li class="listitem"><p class="simpara"><span class="strong"><strong>contractRepository</strong></span> - closure where you can define properties related to repository with contracts</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><span class="strong"><strong>username</strong></span> - username to be used to connect to the repo</li><li class="listitem"><span class="strong"><strong>password</strong></span> - username to be used to connect to the repo</li><li class="listitem"><span class="strong"><strong>proxyHost</strong></span> - proxy host to be used to connect to the repo</li><li class="listitem"><span class="strong"><strong>proxyPort</strong></span> - proxy port to be used to connect to the repo</li><li class="listitem"><span class="strong"><strong>cacheDownloadedContracts</strong></span> - if you want to reuse download JARs that contain contract definitions.
We cache only non-snapshot, explicitly provided versions (e.g. <code class="literal">+</code> or <code class="literal">1.0.0.BUILD-SNAPSHOT</code> won&#8217;t get cached).
By default this feature is turned on.</li></ul></div></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_single_base_class_for_all_tests_2" href="#_single_base_class_for_all_tests_2"></a>Single base class for all tests</h5></div></div></div><p>When using Spring Cloud Contract Verifier in default MockMvc you need to create a base specification for all generated acceptance tests.
In this class you need to point to endpoint which should be verified.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.mycompany.tests
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.mycompany.ExampleSpringController
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.restassured.module.mockmvc.RestAssuredMockMvc
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> spock.lang.Specification
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MvcSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ExampleSpringController())
}
}</pre><p>In case of using <code class="literal">Explicit</code> mode, you can use base class to initialize the whole tested app similarly as in regular integration tests. In case of <code class="literal">JAXRSCLIENT</code> mode this base class should also contain <code class="literal">protected WebTarget webTarget</code> field, right now the only option to test JAX-RS API is to start a web server.</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_different_base_classes_for_contracts_2" href="#_different_base_classes_for_contracts_2"></a>Different base classes for contracts</h5></div></div></div><p>If your base classes differ between contracts you can tell the Spring Cloud Contract plugin which class should get
extended by the autogenerated tests. You have two options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">follow a convention by providing the <code class="literal">packageWithBaseClasses</code></li><li class="listitem">provide explicit mapping via <code class="literal">baseClassMappings</code></li></ul></div><p><span class="strong"><strong>Convention</strong></span></p><p>The convention is such that if you have a contract under e.g. <code class="literal">src/test/resources/contract/hello/v1/</code> and provide the value of the <code class="literal">packageWithBaseClasses</code> property
to <code class="literal">hello</code> then we will assume that there is a <code class="literal">HelloV1Base</code> class under <code class="literal">hello</code> package. In other words we take last two parts of package
if they exist and form a class with a <code class="literal">Base</code> suffix. Takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>. Example of usage:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>hello<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p><span class="strong"><strong>Mapping</strong></span></p><p>You can manually map a regular expression of the contract&#8217;s package to fully qualified name of the base class for the matched contract.
You have to provide a list <code class="literal">baseClassMappings</code> of <code class="literal">baseClassMapping</code> that takes a <code class="literal">contractPackageRegex</code> to <code class="literal">baseClassFQN</code> mapping.
Let&#8217;s take a look at the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassForTests&gt;</span>com.example.FooBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassForTests&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*com.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.TestBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Let&#8217;s assume that you have contracts under
- <code class="literal">src/test/resources/contract/com/</code>
- <code class="literal">src/test/resources/contract/foo/</code></p><p>By providing the <code class="literal">baseClassForTests</code> we have a fallback in case mapping didn&#8217;t succeed (you could also provide
the <code class="literal">packageWithBaseClasses</code> as fallback). That way the tests generated from <code class="literal">src/test/resources/contract/com/</code> contracts
will be extending the <code class="literal">com.example.ComBase</code> whereas the rest of tests will extend <code class="literal">com.example.FooBase</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_invoking_generated_tests_2" href="#_invoking_generated_tests_2"></a>Invoking generated tests</h4></div></div></div><p>Spring Cloud Contract Maven Plugin generates verification code into directory <code class="literal">/generated-test-sources/contractVerifier</code> and attach this directory to <code class="literal">testCompile</code> goal.</p><p>For Groovy Spock code use:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.codehaus.gmavenplus<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>gmavenplus-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.5<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>testCompile<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSources&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.basedir}/src/test/groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/generated-test-sources/contractVerifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSources&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>To ensure that provider side is complaint with defined contracts, you need to invoke <code class="literal">mvn generateTest test</code></p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_faq_with_maven_plugin" href="#_faq_with_maven_plugin"></a>FAQ with Maven Plugin</h4></div></div></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_maven_plugin_and_sts" href="#_maven_plugin_and_sts"></a>Maven Plugin and STS</h5></div></div></div><p>In case you see the following exception while using STS</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/2.0.x/docs/src/main/asciidoc/images/sts_exception.png" alt="STS Exception"></div></div><p>when you click on the marker you should see sth like this</p><pre class="programlisting"> plugin:<span class="hl-number">1.1</span>.<span class="hl-number">0.</span>M1:convert:default-convert:process-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
cloud-contract-maven-plugin:<span class="hl-number">1.1</span>.<span class="hl-number">0.</span>M1:convert failed. at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:<span class="hl-number">145</span>) at
org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:<span class="hl-number">331</span>) at org.eclipse.m2e.core.internal.embedder.MavenImpl$<span class="hl-number">11.</span>call(MavenImpl.java:<span class="hl-number">1362</span>) at
...
org.eclipse.core.internal.jobs.Worker.run(Worker.java:<span class="hl-number">55</span>) Caused by: java.lang.NullPointerException at
org.eclipse.m2e.core.internal.builder.plexusbuildapi.EclipseIncrementalBuildContext.hasDelta(EclipseIncrementalBuildContext.java:<span class="hl-number">53</span>) at
org.sonatype.plexus.build.incremental.ThreadBuildContext.hasDelta(ThreadBuildContext.java:<span class="hl-number">59</span>) at</pre><p>In order to fix this issue just provide the following section in your <code class="literal">pom.xml</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.eclipse.m2e<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>lifecycle-mapping<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;lifecycleMappingMetadata&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginExecutions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginExecution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginExecutionFilter&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;versionRange&gt;</span>[1.0,)<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/versionRange&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>convert<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginExecutionFilter&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;action&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execute /&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/action&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginExecution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginExecutions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/lifecycleMappingMetadata&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span></pre></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_cloud_contract_verifier_on_consumer_side_2" href="#_spring_cloud_contract_verifier_on_consumer_side_2"></a>Spring Cloud Contract Verifier on consumer side</h4></div></div></div><p>You can actually use the Spring Cloud Contract Verifier also for the consumer side!
You can use the plugin so that it only converts the contracts and generates the stubs.
To achieve that you need to configure Spring Cloud Contract Verifier plugin in exactly
the same way as in case of provider. You need to copy contracts stored in
<code class="literal">src/test/resources/contracts</code> and generate WireMock json stubs using:
<code class="literal">mvn generateStubs</code> command. By default generated WireMock mapping is
stored in directory <code class="literal">target/mappings</code>. Your project should create from
this generated mappings additional artifact with classifier <code class="literal">stubs</code> for
easy deploy to maven repository.</p><p>Sample configuration:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${verifier-plugin.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>convert<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>generateStubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>When present, json stubs can be used in consumer automated tests.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringTestRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
LoanApplicationService service;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> shouldSuccessfullyApplyForLoan() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//given:</span>
LoanApplication application =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoanApplication(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Client(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"12345678901"</span>), <span class="hl-number">123.123</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//when:</span>
LoanApplicationResult loanApplication = service.loanApplication(application);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(loanApplication.loanApplicationStatus).isEqualTo(LoanApplicationStatus.LOAN_APPLIED);
assertThat(loanApplication.rejectionReason).isNull();
}
}</pre><p>Underneath <code class="literal">LoanApplication</code> makes a call to the <code class="literal">FraudDetection</code> service. This request is handled by
a WireMock server configured using stubs generated by Spring Cloud Contract Verifier.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_scenarios" href="#_scenarios"></a>2.3.3&nbsp;Scenarios</h3></div></div></div><p>It&#8217;s possible to handle scenarios with Spring Cloud Contract Verifier. All you need to do is to stick to proper naming convention while creating your contracts. The convention requires to include order number followed by the underscore.</p><pre class="screen">my_contracts_dir\
scenario1\
1_login.groovy
2_showCart.groovy
3_logout.groovy</pre><p>Such tree will cause Spring Cloud Contract Verifier generating WireMock&#8217;s scenario with name <code class="literal">scenario1</code> and three steps:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">login marked as <code class="literal">Started</code> pointing to:</li><li class="listitem">showCart marked as <code class="literal">Step1</code> pointing to:</li><li class="listitem">logout marked as <code class="literal">Step2</code> which will close the scenario.</li></ul></div><p>More details about WireMock scenarios can be found under <a class="link" href="http://wiremock.org/stateful-behaviour.html" target="_top">http://wiremock.org/stateful-behaviour.html</a></p><p>Spring Cloud Contract Verifier will also generate tests with guaranteed order of execution.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stubs_and_transitive_dependencies" href="#_stubs_and_transitive_dependencies"></a>2.3.4&nbsp;Stubs and transitive dependencies</h3></div></div></div><p>The Maven and Gradle plugin that we&#8217;re created are adding the tasks that create the stubs jar for you. What can be problematic
is that when reusing the stubs you can by mistake import all of that stub dependencies! When building a Maven artifact
even though you have a couple of different jars, all of them share one pom:</p><pre class="programlisting">&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075506</span>-<span class="hl-number">1</span>-stubs.jar
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075506</span>-<span class="hl-number">1</span>-stubs.jar.sha1
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075655</span>-<span class="hl-number">2</span>-stubs.jar
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075655</span>-<span class="hl-number">2</span>-stubs.jar.sha1
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-SNAPSHOT.jar
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-SNAPSHOT.pom
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-SNAPSHOT-stubs.jar
&#9500;&#9472;&#9472; ...
&#9492;&#9472;&#9472; ...</pre><p>There are three possibilities of working with those dependencies so as not to have any issues with transitive dependencies.</p><p><span class="strong"><strong>Mark all application dependencies as optional</strong></span></p><p>If in the <code class="literal">github-webhook</code> application we would mark all of our dependencies as optional, when you include the
<code class="literal">github-webhook</code> stubs in another application (or when that dependency gets downloaded by Stub Runner) then, since
all of the depenencies are optional, they will not get downloaded.</p><p><span class="strong"><strong>Create a separate artifactid for stubs</strong></span></p><p>If you create a separate artifactid then you can set it up in whatever way you wish. For example by having no dependencies at all.</p><p><span class="strong"><strong>Exclude dependencies on the consumer side</strong></span></p><p>As a consumer, if you add the stub dependency to your classpath you can explicitly exclude the unwanted dependencies.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spring_cloud_contract_verifier_messaging" href="#_spring_cloud_contract_verifier_messaging"></a>2.4&nbsp;Spring Cloud Contract Verifier Messaging</h2></div></div></div><p>Spring Cloud Contract Verifier allows you to verify your application that uses messaging as means of communication.
All of our integrations are working with Spring but you can also create one yourself and use it.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_integrations" href="#_integrations"></a>2.4.1&nbsp;Integrations</h3></div></div></div><p>You can use one of the four integration configurations:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Apache Camel</li><li class="listitem">Spring Integration</li><li class="listitem">Spring Cloud Stream</li><li class="listitem">Spring AMQP</li></ul></div><p>Since we&#8217;re using Spring Boot then if you have added one of the aforementioned libraries
to the classpath then automatically all the messaging configuration will be set up.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember to put <code class="literal">@AutoConfigureMessageVerifier</code> on the base class of your
generated tests. Otherwise messaging part of Spring Cloud Contract Verifier will not work.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you want to use Spring Cloud Stream remember to add a
<code class="literal">org.springframework.cloud:spring-cloud-stream-test-support</code> dependency.</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-stream-test-support"</span></pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_manual_integration_testing" href="#_manual_integration_testing"></a>2.4.2&nbsp;Manual Integration Testing</h3></div></div></div><p>The main interface used by the tests is the <code class="literal">org.springframework.cloud.contract.verifier.messaging.MessageVerifier</code>.
It defines how to send and receive messages. You can create your own implementation to achieve the
same goal.</p><p>In the a test you can inject a <code class="literal">ContractVerifierMessageExchange</code> to send and receive messages that follow the contract.
Then add <code class="literal">@AutoConfigureMessageVerifier</code> to your test, e.g.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringTestRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureMessageVerifier</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MessagingContractTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageVerifier verifier;
...
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If your tests require stubs as well, then
<code class="literal">@AutoConfigureStubRunner</code> includes the messaging configuration, so
you only need the one annotation.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_publisher_side_test_generation" href="#_publisher_side_test_generation"></a>2.4.3&nbsp;Publisher side test generation</h3></div></div></div><p>Having the <code class="literal">input</code> or <code class="literal">outputMessage</code> sections in your DSL will result in creation of tests on the publisher&#8217;s side. By default
JUnit tests will be created, however there is also a possibility to create Spock tests.</p><p>There are 3 main scenarios that we should take into consideration:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Scenario 1: there is no input message that produces an output one. The output message is triggered by a component
inside the application (e.g. scheduler)</li><li class="listitem">Scenario 2: the input message triggers an output message</li><li class="listitem">Scenario 3: the input message is consumed and there is no output message</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The destination passed to <code class="literal">messageFrom</code> or <code class="literal">sentTo</code> can have different meanings for different
messaging implementations. For <span class="strong"><strong>Stream</strong></span> and <span class="strong"><strong>Integration</strong></span> it&#8217;s first resolved as a <code class="literal">destination</code> of a channel, and then if
there is no such <code class="literal">destination</code> it&#8217;s resolved as a channel name. For <span class="strong"><strong>Camel</strong></span> that&#8217;s a certain component (e.x. <code class="literal">jms</code>).</p></td></tr></table></div><p>Example for Camel:</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_1_no_input_message" href="#_scenario_1_no_input_message"></a>Scenario 1 (no input message)</h4></div></div></div><p>For the given contract:</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'activemq:output'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
messagingContentType(applicationJson())
}
}
}</pre><p>The following JUnit test will be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
bookReturnedTriggered();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"activemq:output"</span>);
assertThat(response).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>)).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>).toString()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contentType"</span>)).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contentType"</span>).toString()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()));
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><p>And the following Spock test would be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> when:
bookReturnedTriggered()
then:
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'activemq:output'</span>)
assert response != null
response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>)?.toString() == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contentType'</span>)?.toString() == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
and:
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_2_output_triggered_by_input" href="#_scenario_2_output_triggered_by_input"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>For the given contract:</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>The following JUnit test will be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>bookName\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>foo\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"}"</span>
, headers()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sample"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"header"</span>));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"jms:input"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"jms:output"</span>);
assertThat(response).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>)).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>).toString()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()));
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><p>And the following Spock test would be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\
</span>given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{"bookName":"foo"}'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>,
[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>]
)
when:
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>)
then:
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
assert response !- null
response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>)?.toString() == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
and:
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_3_no_output_message" href="#_scenario_3_no_output_message"></a>Scenario 3 (no output message)</h4></div></div></div><p>For the given contract:</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookWasDeleted()'</span>)
}
}</pre><p>The following JUnit test will be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>bookName\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>foo\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"}"</span>
, headers()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sample"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"header"</span>));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"jms:delete"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
bookWasDeleted();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><p>And the following Spock test would be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span>given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'\'\'{"bookName":"foo"}\'\'\',
</span> [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>]
)
when:
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>)
then:
noExceptionThrown()
bookWasDeleted()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer_stub_side_generation" href="#_consumer_stub_side_generation"></a>2.4.4&nbsp;Consumer Stub Side generation</h3></div></div></div><p>Unlike the HTTP part - in Messaging we need to publish the Groovy DSL inside the JAR with a stub. Then it&#8217;s parsed on the consumer side
and proper stubbed routes are created.</p><p>For more information please consult the Stub Runner Messaging sections.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-stream-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Finchley.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">ext {
contractsDir = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"mappings"</span>)
stubsOutputDirRoot = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/production/${project.name}-stubs/"</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Automatically added by plugin:</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// copyContracts - copies contracts to the output folder from which JAR will be created</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// verifierStubsJar - JAR with a provided stub suffix</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the presented publication is also added by the plugin but you can modify it as you wish</span>
publishing {
publications {
stubs(MavenPublication) {
artifactId <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.name}-stubs"</span>
artifact verifierStubsJar
}
}
}</pre><p class="secondary">
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spring_cloud_contract_stub_runner" href="#_spring_cloud_contract_stub_runner"></a>2.5&nbsp;Spring Cloud Contract Stub Runner</h2></div></div></div><p>One of the issues that you could have encountered while using Spring Cloud Contract Verifier was to pass the generated WireMock JSON stubs from the server side to the client side (or various clients).
The same takes place in terms of client side generation for messaging.</p><p>Copying the JSON files / setting the client side for messaging manually is out of the question.</p><p>That&#8217;s why we&#8217;ll introduce Spring Cloud Contract Stub Runner that can download and run the stubs
automatically for you.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_snapshot_versions" href="#_snapshot_versions"></a>2.5.1&nbsp;Snapshot versions</h3></div></div></div><p>Add the additional snapshot repository to your build.gradle to use snapshot versions which are automatically uploaded after every successful build:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/snapshot"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/milestone"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://repo.spring.io/release"</span> }
}</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_publishing_stubs_as_jars" href="#_publishing_stubs_as_jars"></a>2.5.2&nbsp;Publishing stubs as JARs</h3></div></div></div><p>The easiest approach would be to centralize the way stubs are kept. For example you can keep them as JARs in a Maven repository.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>For both Maven and Gradle the setup comes out of the box. But you can customize it if you want to.</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- First disable the default jar setup in the properties section--&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- we don't want the verifier to do a jar for us --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring.cloud.contract.verifier.skip&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring.cloud.contract.verifier.skip&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Next add the assembly plugin to your build --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- we want the assembly plugin to generate the JAR --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.maven.plugins<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>maven-assembly-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>stub<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>prepare-package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>single<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;inherited&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/inherited&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;attach&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/attach&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;descriptor&gt;</span>${basedir}/src/assembly/stub.xml<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/descriptor&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Finally setup your assembly. Below you can find the contents of src/main/assembly/stub.xml --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;assembly</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 https://maven.apache.org/xsd/assembly-1.1.3.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;format&gt;</span>jar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/format&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includeBaseDirectory&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includeBaseDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>src/main/java<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**com/example/model/*.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/classes<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**com/example/model/*.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/snippets/stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${basedir}/src/test/resources/contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>META-INF/${project.groupId}/${project.artifactId}/${project.version}/contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/assembly&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">ext {
contractsDir = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"mappings"</span>)
stubsOutputDirRoot = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/production/${project.name}-stubs/"</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Automatically added by plugin:</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// copyContracts - copies contracts to the output folder from which JAR will be created</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// verifierStubsJar - JAR with a provided stub suffix</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the presented publication is also added by the plugin but you can modify it as you wish</span>
publishing {
publications {
stubs(MavenPublication) {
artifactId <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.name}-stubs"</span>
artifact verifierStubsJar
}
}
}</pre><p class="secondary">
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_core" href="#_stub_runner_core"></a>2.6&nbsp;Stub Runner Core</h2></div></div></div><p>Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of
<a class="link" href="https://martinfowler.com/articles/consumerDrivenContracts.html" target="_top">Consumer Driven Contracts</a>.</p><p>Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions.
For messaging, special stub routes are defined.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_retrieving_stubs" href="#_retrieving_stubs"></a>2.6.1&nbsp;Retrieving stubs</h3></div></div></div><p>You can pick the following options of acquiring stubs</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Aether based solution that downloads JARs with stubs from Artifactory / Nexus</li><li class="listitem">Classpath scanning solution that searches classpath via pattern to retrieve stubs</li><li class="listitem">Write your own implementation of the <code class="literal">org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder</code> for full customization</li></ul></div><p>The latter example is described in the <a class="link" href="">Custom Stub Runner</a> section.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stub_downloading" href="#_stub_downloading"></a>Stub downloading</h4></div></div></div><p>If you provide the <code class="literal">stubrunner.repositoryRoot</code> or <code class="literal">stubrunner.workOffline</code> flag will be set
to <code class="literal">true</code> then Stub Runner will connect to the given server and download the required jars.
It will then unpack the JAR to a temporary folder and reference those files in further
contract processing.</p><p>Example:</p><pre class="programlisting">@AutoConfigureStubRunner(repositoryRoot=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://foo.bar"</span>, ids = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-api-producer:+:stubs:8095"</span>)</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_classpath_scanning" href="#_classpath_scanning"></a>Classpath scanning</h4></div></div></div><p>If you <span class="strong"><strong>DON&#8217;T</strong></span> provide the <code class="literal">stubrunner.repositoryRoot</code> and <code class="literal">stubrunner.workOffline</code> flag will
be set to <code class="literal">false</code> (that&#8217;s the default) then classpath will get scanned. Let&#8217;s look at the
following example:</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-api-producer:+:stubs:8095"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example.foo:bar:1.0.0:superstubs:8096"</span>
})</pre><p>If you&#8217;ve added the dependencies to your classpath</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-api-producer-restdocs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;classifier&gt;</span>stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/classifier&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>0.0.1-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example.foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>bar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;classifier&gt;</span>superstubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/classifier&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-api-producer-restdocs:0.0.1-SNAPSHOT:stubs"</span>) {
transitive = false
}
testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example.foo:bar:1.0.0:superstubs"</span>) {
transitive = false
}</pre><p class="secondary">
</p><p>Then the following locations on your classpath will get scanned. For <code class="literal">com.example:beer-api-producer-restdocs</code></p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">/META-INF/com.example/beer-api-producer-restdocs/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/contracts/com.example/beer-api-producer-restdocs/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/mappings/com.example/beer-api-producer-restdocs/<span class="strong"><strong>*/</strong></span>.*</li></ul></div><p>and <code class="literal">com.example.foo:bar</code></p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">/META-INF/com.example.foo/bar/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/contracts/com.example.foo/bar/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/mappings/com.example.foo/bar/<span class="strong"><strong>*/</strong></span>.*</li></ul></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>As you can see you have to explicitly provide the group and artifact ids when packaging the
producer stubs.</p></td></tr></table></div><p>The producer would setup the contracts like this:</p><pre class="programlisting">&#9492;&#9472;&#9472; src
&#9492;&#9472;&#9472; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>
&#9492;&#9472;&#9472; resources
&#9492;&#9472;&#9472; contracts
&nbsp;&nbsp; &#9492;&#9472;&#9472; com.example
&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; beer-api-producer-restdocs
&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; nested
&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; contract3.groovy</pre><p>To achieve proper stub packaging.</p><p>Or using the <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/producer_with_restdocs/pom.xml" target="_top">Maven <code class="literal">assembly</code> plugin</a> or
<a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/producer_with_restdocs/build.gradle" target="_top">Gradle Jar</a> task you have to create the following
structure in your stubs jar.</p><pre class="programlisting">&#9492;&#9472;&#9472; META-INF
&#9492;&#9472;&#9472; com.example
&#9492;&#9472;&#9472; beer-api-producer-restdocs
&#9492;&#9472;&#9472; <span class="hl-number">2.0</span>.<span class="hl-number">0</span>
&#9500;&#9472;&#9472; contracts
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; nested
&nbsp;&nbsp; &#9474; &#9492;&#9472;&#9472; contract2.groovy
&nbsp;&nbsp; &#9492;&#9472;&#9472; mappings
&nbsp;&nbsp; &#9492;&#9472;&#9472; mapping.json</pre><p>By maintaining this structure classpath gets scanned and you can profit from the messaging /
HTTP stubs without the need to download artifacts.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_running_stubs" href="#_running_stubs"></a>2.6.2&nbsp;Running stubs</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_limitations" href="#_limitations"></a>Limitations</h4></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>There might be a problem with StubRunner shutting down ports between tests. You might
have a situation in which you get port conflicts. As long as you use the same context across tests
everything works fine. But when the context are different (e.g. different stubs or different profiles)
then you have to either use <code class="literal">@DirtiesContext</code> to shut down the stub servers, or else run them on
different ports per test.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_running_using_main_app" href="#_running_using_main_app"></a>Running using main app</h4></div></div></div><p>You can set the following options to the main class:</p><pre class="programlisting">-c, --classifier Suffix <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> the jar containing stubs (e.
g. <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'stubs'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> the stub jar would
have a <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'stubs'</span> classifier <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> stubs:
foobar-stubs ). Defaults to <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'stubs'</span>
(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>: stubs)
--maxPort, --maxp &lt;Integer&gt; Maximum port value to be assigned to
the WireMock instance. Defaults to
<span class="hl-number">15000</span> (<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>: <span class="hl-number">15000</span>)
--minPort, --minp &lt;Integer&gt; Minimum port value to be assigned to
the WireMock instance. Defaults to
<span class="hl-number">10000</span> (<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>: <span class="hl-number">10000</span>)
-p, --password Password to user when connecting to
repository
--phost, --proxyHost Proxy host to use <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> repository
requests
--pport, --proxyPort [Integer] Proxy port to use <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> repository
requests
-r, --root Location of a Jar containing server
where you keep your stubs (e.g. http:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//nexus.</span>
net/content/repositories/repository)
-s, --stubs Comma separated list of Ivy
representation of jars with stubs.
Eg. groupid:artifactid1,groupid2:
artifactid2:classifier
-u, --username Username to user when connecting to
repository
--wo, --workOffline Switch to work offline. Defaults to
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'false'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_http_stubs" href="#_http_stubs"></a>HTTP Stubs</h4></div></div></div><p>Stubs are defined in JSON documents, whose syntax is defined in <a class="link" href="http://wiremock.org/stubbing.html" target="_top">WireMock documentation</a></p><p>Example:</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/ping"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span class="hl-number">200</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pong"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_viewing_registered_mappings" href="#_viewing_registered_mappings"></a>Viewing registered mappings</h4></div></div></div><p>Every stubbed collaborator exposes list of defined mappings under <code class="literal">__/admin/</code> endpoint.</p><p>You can also use the <code class="literal">mappingsOutputFolder</code> property to dump the mappings to files.
For annotation based approach it would look like this</p><pre class="programlisting">@AutoConfigureStubRunner(ids=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a.b.c:loanIssuance,a.b.c:fraudDetectionServer"</span>,
mappingsOutputFolder = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappings/"</span>)</pre><p>and for the JUnit approach like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <em><span class="hl-annotation" style="color: gray">@Shared</span></em> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://some_url"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a.b.c"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a.b.c:fraudDetectionServer"</span>)
.withMappingsOutputFolder(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappings"</span>)</pre><p>Then if you check out the folder <code class="literal">target/outputmappings</code> you would see the following structure</p><pre class="programlisting">.
&#9500;&#9472;&#9472; fraudDetectionServer_<span class="hl-number">13705</span>
&#9492;&#9472;&#9472; loanIssuance_<span class="hl-number">12255</span></pre><p>That means that there were two stubs registered. <code class="literal">fraudDetectionServer</code> was registered at port <code class="literal">13705</code>
and <code class="literal">loanIssuance</code> at port <code class="literal">12255</code>. If we take a look at one of the files we would see (for WireMock)
mappings available for the given server:</p><pre class="programlisting">[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"f9152eb9-bf77-4c38-8289-90be7d10d0d7"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/name"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"uuid"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"f9152eb9-bf77-4c38-8289-90be7d10d0d7"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
...
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_messaging_stubs" href="#_messaging_stubs"></a>Messaging Stubs</h4></div></div></div><p>Depending on the provided Stub Runner dependency and the DSL the messaging routes are automatically set up.</p></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_junit_rule" href="#_stub_runner_junit_rule"></a>2.7&nbsp;Stub Runner JUnit Rule</h2></div></div></div><p>Stub Runner comes with a JUnit rule thanks to which you can very easily download and run stubs for given group and artifact id:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(repoRoot())
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>);</pre><p>After that rule gets executed Stub Runner connects to your Maven repository and for the given list of dependencies tries to:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">download them</li><li class="listitem">cache them locally</li><li class="listitem">unzip them to a temporary folder</li><li class="listitem">start a WireMock server for each Maven dependency on a random port from the provided range of ports / provided port</li><li class="listitem">feed the WireMock server with all JSON files that are valid WireMock definitions</li><li class="listitem">can also send messages (remember to pass an implementation of <code class="literal">MessageVerifier</code> interface)</li></ul></div><p>Stub Runner uses <a class="link" href="https://wiki.eclipse.org/Aether" target="_top">Eclipse Aether</a> mechanism to download the Maven dependencies.
Check their <a class="link" href="https://wiki.eclipse.org/Aether" target="_top">docs</a> for more information.</p><p>Since the <code class="literal">StubRunnerRule</code> implements the <code class="literal">StubFinder</code> it allows you to find the started stubs:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.stubrunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.net.URL;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Collection;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Map;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StubFinder <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> StubTrigger {
<strong class="hl-tag" style="color: blue">/**
* For the given groupId and artifactId tries to find the matching
* URL of the running stub.
*
* @param groupId - might be null. In that case a search only via artifactId takes place
* @return URL of a running stub or throws exception if not found
*/</strong>
URL findStubUrl(String groupId, String artifactId) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> StubNotFoundException;
<strong class="hl-tag" style="color: blue">/**
* For the given Ivy notation {@code [groupId]:artifactId:[version]:[classifier]} tries to
* find the matching URL of the running stub. You can also pass only {@code artifactId}.
*
* @param ivyNotation - Ivy representation of the Maven artifact
* @return URL of a running stub or throws exception if not found
*/</strong>
URL findStubUrl(String ivyNotation) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> StubNotFoundException;
<strong class="hl-tag" style="color: blue">/**
* Returns all running stubs
*/</strong>
RunningStubs findAllRunningStubs();
<strong class="hl-tag" style="color: blue">/**
* Returns the list of Contracts
*/</strong>
Map&lt;StubConfiguration, Collection&lt;Contract&gt;&gt; getContracts();
}</pre><p>Example of usage in Spock tests:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <em><span class="hl-annotation" style="color: gray">@Shared</span></em> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(StubRunnerRuleSpec.getResource(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/m2repo/repository"</span>).toURI().toString())
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)
.withMappingsOutputFolder(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappingsforrule"</span>)
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should start WireMock servers'</span>() {
expect: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'WireMocks are running'</span>
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) == rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>) != null
and:
rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>)
rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>)
and: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Stubs were registered'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${rule.findStubUrl('loanIssuance').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${rule.findStubUrl('fraudDetectionServer').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should output mappings to output folder'</span>() {
when:
def url = rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>)
then:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappingsforrule"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer_${url.port}"</span>).exists()
}</pre><p>Example of usage in JUnit tests:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> should_start_wiremock_servers() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// expect: 'WireMocks are running'</span>
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isNotNull();
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isNotNull();
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isEqualTo(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>));
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)).isNotNull();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
then(rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isTrue();
then(rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)).isTrue();
then(rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)).isTrue();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and: 'Stubs were registered'</span>
then(httpGet(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>).toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/name"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>);
then(httpGet(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>).toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/name"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>);
}</pre><p>Check the <span class="strong"><strong>Common properties for JUnit and Spring</strong></span> for more information on how to apply global configuration of Stub Runner.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>To use the JUnit rule together with messaging you have to provide an implementation of the
<code class="literal">MessageVerifier</code> interface to the rule builder (e.g. <code class="literal">rule.messageVerifier(new MyMessageVerifier())</code>).
If you don&#8217;t do this then whenever you try to send a message an exception will be thrown.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_maven_settings" href="#_maven_settings"></a>2.7.1&nbsp;Maven settings</h3></div></div></div><p>The stub downloader honors Maven settings for a different local repository folder.
Authentication details for repositories and profiles are currently not taken into account, so you need to specify it using the properties mentioned above.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_providing_fixed_ports" href="#_providing_fixed_ports"></a>2.7.2&nbsp;Providing fixed ports</h3></div></div></div><p>You can also run your stubs on fixed ports. You can do it in two different ways. One is to pass it in the properties, and the other via fluent API of
JUnit rule.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_fluent_api" href="#_fluent_api"></a>2.7.3&nbsp;Fluent API</h3></div></div></div><p>When using the <code class="literal">StubRunnerRule</code> you can add a stub to download and then pass the port for the last downloaded stub.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(repoRoot())
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.withPort(<span class="hl-number">12345</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer:12346"</span>);</pre><p>You can see that for this example the following test is valid:</p><pre class="programlisting">then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isEqualTo(URI.create(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:12345"</span>).toURL());
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)).isEqualTo(URI.create(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:12346"</span>).toURL());</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stub_runner_with_spring" href="#_stub_runner_with_spring"></a>2.7.4&nbsp;Stub Runner with Spring</h3></div></div></div><p>Sets up Spring configuration of the Stub Runner project.</p><p>By providing a list of stubs inside your configuration file the Stub Runner automatically downloads
and registers in WireMock the selected stubs.</p><p>If you want to find the URL of your stubbed dependency you can autowire the <code class="literal">StubFinder</code> interface and use
its methods as presented below:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(properties = [" stubrunner.cloud.enabled=false",
"stubrunner.camel.enabled=false",
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}'])</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/")</span></em>
<em><span class="hl-annotation" style="color: gray">@DirtiesContext</span></em>
<em><span class="hl-annotation" style="color: gray">@ActiveProfiles("test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerConfigurationSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> StubFinder stubFinder
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Environment environment
<em><span class="hl-annotation" style="color: gray">@Value('${foo}')</span></em> Integer foo
<em><span class="hl-annotation" style="color: gray">@BeforeClass</span></em>
<em><span class="hl-annotation" style="color: gray">@AfterClass</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setupProps() {
System.clearProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.repository.root"</span>)
System.clearProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.classifier"</span>)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should start WireMock servers'</span>() {
expect: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'WireMocks are running'</span>
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) == stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) == stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:loanIssuance'</span>)
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT'</span>) == stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs'</span>)
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>) != null
and:
stubFinder.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
stubFinder.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>)
stubFinder.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>)
and: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Stubs were registered'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('loanIssuance').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should throw an exception when stub is not found'</span>() {
when:
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'nonExistingService'</span>)
then:
thrown(StubNotFoundException)
when:
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'nonExistingGroupId'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'nonExistingArtifactId'</span>)
then:
thrown(StubNotFoundException)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should register started servers as environment variables'</span>() {
expect:
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.loanIssuance.port"</span>) != null
stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>) == (environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.loanIssuance.port"</span>) as Integer)
and:
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.fraudDetectionServer.port"</span>) != null
stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>) == (environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.fraudDetectionServer.port"</span>) as Integer)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should be able to interpolate a running stub in the passed test property'</span>() {
given:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> fraudPort = stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)
expect:
fraudPort &gt; <span class="hl-number">0</span>
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, Integer) == fraudPort
foo == fraudPort
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should dump all mappings to a file'</span>() {
when:
def url = stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)
then:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappings/"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer_${url.port}"</span>).exists()
}
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {}
}</pre><p>for the following configuration file:</p><pre class="programlisting">stubrunner:
repositoryRoot: classpath:m2repo/repository/
ids:
- org.springframework.cloud.contract.verifier.stubs:loanIssuance
- org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer
- org.springframework.cloud.contract.verifier.stubs:bootService
cloud:
enabled: false
camel:
enabled: false
spring.cloud:
consul.enabled: false
service-registry.enabled: false</pre><p>Instead of using the properties you can also use the properties inside the <code class="literal">@AutoConfigureStubRunner</code>.
Below you can find an example of achieving the same result by setting values on the annotation.</p><pre class="programlisting">@AutoConfigureStubRunner(
ids = [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:loanIssuance"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:bootService"</span>],
repositoryRoot = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath:m2repo/repository/"</span>)</pre><p>Stub Runner Spring registers environment variables in the following manner
for every registered WireMock server. Example for Stub Runner ids
<code class="literal">com.example:foo</code>, <code class="literal">com.example:bar</code>.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">stubrunner.runningstubs.foo.port</code></li><li class="listitem"><code class="literal">stubrunner.runningstubs.bar.port</code></li></ul></div><p>Which you can reference in your code.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_spring_cloud" href="#_stub_runner_spring_cloud"></a>2.8&nbsp;Stub Runner Spring Cloud</h2></div></div></div><p>Stub Runner can integrate with Spring Cloud.</p><p>For real life examples you can check the</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/master/producer" target="_top">producer app sample</a></li><li class="listitem"><a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/master/consumer_with_discovery" target="_top">consumer app sample</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stubbing_service_discovery" href="#_stubbing_service_discovery"></a>2.8.1&nbsp;Stubbing Service Discovery</h3></div></div></div><p>The most important feature of <code class="literal">Stub Runner Spring Cloud</code> is the fact that it&#8217;s stubbing</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">DiscoveryClient</code></li><li class="listitem"><code class="literal">Ribbon</code> <code class="literal">ServerList</code></li></ul></div><p>that means that regardless of the fact whether you&#8217;re using Zookeeper, Consul, Eureka or anything else, you don&#8217;t need that in your tests.
We&#8217;re starting WireMock instances of your dependencies and we&#8217;re telling your application whenever you&#8217;re using <code class="literal">Feign</code>, load balanced <code class="literal">RestTemplate</code>
or <code class="literal">DiscoveryClient</code> directly, to call those stubbed servers instead of calling the real Service Discovery tool.</p><p>For example this test will pass</p><pre class="programlisting">def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should make service discovery work'</span>() {
expect: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'WireMocks are running'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('loanIssuance').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
and: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Stubs can be reached via load service discovery'</span>
restTemplate.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://loanIssuance/name'</span>, String) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
restTemplate.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://someNameThatShouldMapFraudDetectionServer/name'</span>, String) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
}</pre><p>for the following configuration file</p><pre class="programlisting">spring.cloud:
zookeeper.enabled: false
consul.enabled: false
eureka.client.enabled: false
stubrunner:
camel.enabled: false
idsToServiceIds:
ivyNotation: someValueInsideYourCode
fraudDetectionServer: someNameThatShouldMapFraudDetectionServer</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_test_profiles_and_service_discovery" href="#_test_profiles_and_service_discovery"></a>Test profiles and service discovery</h4></div></div></div><p>In your integration tests you typically don&#8217;t want to call neither a discovery service (e.g. Eureka)
or Config Server. That&#8217;s why you create an additional test configuration in which you want to disable
these features.</p><p>Due to certain limitations of <a class="link" href="https://github.com/spring-cloud/spring-cloud-commons/issues/156" target="_top"><code class="literal">spring-cloud-commons</code></a> to achieve this you have disable these properties
via a static block like presented below (example for Eureka)</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Hack to work around https://github.com/spring-cloud/spring-cloud-commons/issues/156</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> {
System.setProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"eureka.client.enabled"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"false"</span>);
System.setProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring.cloud.config.failFast"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"false"</span>);
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_additional_configuration" href="#_additional_configuration"></a>2.8.2&nbsp;Additional Configuration</h3></div></div></div><p>You can match the artifactId of the stub with the name of your app by using the <code class="literal">stubrunner.idsToServiceIds:</code> map.
You can disable Stub Runner Ribbon support by providing: <code class="literal">stubrunner.cloud.ribbon.enabled</code> equal to <code class="literal">false</code>
You can disable Stub Runner support by providing: <code class="literal">stubrunner.cloud.enabled</code> equal to <code class="literal">false</code></p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>By default all service discovery will be stubbed. That means that regardless of the fact if you have
an existing <code class="literal">DiscoveryClient</code> its results will be ignored. However, if you want to reuse it, just set
<code class="literal">stubrunner.cloud.delegate.enabled</code> to <code class="literal">true</code> and then your existing <code class="literal">DiscoveryClient</code> results will be
merged with the stubbed ones.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_boot_application" href="#_stub_runner_boot_application"></a>2.9&nbsp;Stub Runner Boot Application</h2></div></div></div><p>Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to
trigger the messaging labels and to access started WireMock servers.</p><p>One of the use-cases is to run some smoke (end to end) tests on a deployed application.
You can check out the <a class="link" href="https://github.com/spring-cloud/spring-cloud-pipelines" target="_top">Spring Cloud Pipelines</a>
project for more information.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_to_use_it" href="#_how_to_use_it"></a>2.9.1&nbsp;How to use it?</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stub_runner_server" href="#_stub_runner_server"></a>Stub Runner Server</h4></div></div></div><p>Just add the</p><pre class="programlisting">compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-stub-runner"</span></pre><p>Annotate a class with <code class="literal">@EnableStubRunnerServer</code>, build a fat-jar and you&#8217;re ready to go!</p><p>For the properties check the <span class="strong"><strong>Stub Runner Spring</strong></span> section.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_cloud_cli" href="#_spring_cloud_cli"></a>Spring Cloud CLI</h4></div></div></div><p>Starting from <code class="literal">1.4.0.RELEASE</code> version of the <a class="link" href="https://cloud.spring.io/spring-cloud-cli" target="_top">Spring Cloud CLI</a>
project you can start Stub Runner Boot by executing <code class="literal">spring cloud stubrunner</code>.</p><p>In order to pass the configuration just create a <code class="literal">stubrunner.yml</code> file in the current working directory
or a subdirectory called <code class="literal">config</code> or in <code class="literal">~/.spring-cloud</code>. The file could look like this
(example for running stubs installed locally)</p><p><b>stubrunner.yml.&nbsp;</b>
</p><pre class="programlisting">stubrunner:
workOffline: true
ids:
- com.example:beer-api-producer:+:9876</pre><p>
</p><p>and then just call <code class="literal">spring cloud stubrunner</code> from your terminal window to start
the Stub Runner server. It will be available at port <code class="literal">8750</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_endpoints" href="#_endpoints"></a>2.9.2&nbsp;Endpoints</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_http" href="#_http"></a>HTTP</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">GET <code class="literal">/stubs</code> - returns a list of all running stubs in <code class="literal">ivy:integer</code> notation</li><li class="listitem">GET <code class="literal">/stubs/{ivy}</code> - returns a port for the given <code class="literal">ivy</code> notation (when calling the endpoint <code class="literal">ivy</code> can also be <code class="literal">artifactId</code> only)</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_messaging" href="#_messaging"></a>Messaging</h4></div></div></div><p>For Messaging</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">GET <code class="literal">/triggers</code> - returns a list of all running labels in <code class="literal">ivy : [ label1, label2 &#8230;&#8203;]</code> notation</li><li class="listitem">POST <code class="literal">/triggers/{label}</code> - executes a trigger with <code class="literal">label</code></li><li class="listitem">POST <code class="literal">/triggers/{ivy}/{label}</code> - executes a trigger with <code class="literal">label</code> for the given <code class="literal">ivy</code> notation (when calling the endpoint <code class="literal">ivy</code> can also be <code class="literal">artifactId</code> only)</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_example" href="#_example"></a>2.9.3&nbsp;Example</h3></div></div></div><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = StubRunnerBoot, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(properties = "spring.cloud.zookeeper.enabled=false")</span></em>
<em><span class="hl-annotation" style="color: gray">@ActiveProfiles("test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerBootSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> StubRunning stubRunning
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpStubsController(stubRunning),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TriggerController(stubRunning))
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return a list of running stub servers in "full ivy:port" notation'</span>() {
when:
String response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/stubs'</span>).body.asString()
then:
def root = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> JsonSlurper().parseText(response)
root.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">instanceof</span> Integer
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return a port on which a [#stubId] stub is running'</span>() {
when:
def response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stubs/${stubId}"</span>)
then:
response.statusCode == <span class="hl-number">200</span>
response.body.as(Integer) &gt; <span class="hl-number">0</span>
where:
stubId &lt;&lt; [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:+:stubs'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:+'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bootService'</span>]
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return 404 when missing stub was called'</span>() {
when:
def response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stubs/a:b:c:d"</span>)
then:
response.statusCode == <span class="hl-number">404</span>
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return a list of messaging labels that can be triggered when version and classifier are passed'</span>() {
when:
String response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/triggers'</span>).body.asString()
then:
def root = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> JsonSlurper().parseText(response)
root.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'</span>?.containsAll([<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"delete_book"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"return_book_1"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"return_book_2"</span>])
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should trigger a messaging label'</span>() {
given:
StubRunning stubRunning = Mock()
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpStubsController(stubRunning), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TriggerController(stubRunning))
when:
def response = RestAssuredMockMvc.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/triggers/delete_book"</span>)
then:
response.statusCode == <span class="hl-number">200</span>
and:
<span class="hl-number">1</span> * stubRunning.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete_book'</span>)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should trigger a messaging label for a stub with [#stubId] ivy notation'</span>() {
given:
StubRunning stubRunning = Mock()
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpStubsController(stubRunning), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TriggerController(stubRunning))
when:
def response = RestAssuredMockMvc.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/triggers/$stubId/delete_book"</span>)
then:
response.statusCode == <span class="hl-number">200</span>
and:
<span class="hl-number">1</span> * stubRunning.trigger(stubId, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete_book'</span>)
where:
stubId &lt;&lt; [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bootService'</span>]
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should throw exception when trigger is missing'</span>() {
when:
RestAssuredMockMvc.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/triggers/missing_label"</span>)
then:
Exception e = thrown(Exception)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Exception occurred while trying to return [missing_label] label."</span>)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Available labels are"</span>)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]"</span>)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs="</span>)
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stub_runner_boot_with_service_discovery" href="#_stub_runner_boot_with_service_discovery"></a>2.9.4&nbsp;Stub Runner Boot with Service Discovery</h3></div></div></div><p>One of the possibilities of using Stub Runner Boot is to use it as a feed of stubs for "smoke-tests". What does it mean?
Let&#8217;s assume that you don&#8217;t want to deploy 50 microservice to a test environment in order
to check if your application is working fine. You&#8217;ve already executed a suite of tests during the build process
but you would also like to ensure that the packaging of your application is fine. What you can do
is to deploy your application to an environment, start it and run a couple of tests on it to see if
it&#8217;s working fine. We can call those tests smoke-tests since their idea is to check only a handful
of testing scenarios.</p><p>The problem with this approach is such that if you&#8217;re doing microservices most likely you&#8217;re
using a service discovery tool. Stub Runner Boot allows you to solve this issue by starting the
required stubs and register them in a service discovery tool. Let&#8217;s take a look at an example of
such a setup with Eureka. Let&#8217;s assume that Eureka was already running.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableStubRunnerServer</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableEurekaClient</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerBootEurekaExample {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(StubRunnerBootEurekaExample.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p>As you can see we want to start a Stub Runner Boot server <code class="literal">@EnableStubRunnerServer</code>, enable Eureka client <code class="literal">@EnableEurekaClient</code>
and we want to have the stub runner feature turned on <code class="literal">@AutoConfigureStubRunner</code>.</p><p>Now let&#8217;s assume that we want to start this application so that the stubs get automatically registered.
We can do it by running the app <code class="literal">java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar</code> where
<code class="literal">${SYSTEM_PROPS}</code> would contain the following list of properties</p><pre class="programlisting">-Dstubrunner.repositoryRoot=https://repo.spring.io/snapshots (<span class="hl-number">1</span>)
-Dstubrunner.cloud.stubbed.discovery.enabled=false (<span class="hl-number">2</span>)
-Dstubrunner.ids=org.springframework.cloud.contract.verifier.stubs:loanIssuance,org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer,org.springframework.cloud.contract.verifier.stubs:bootService (<span class="hl-number">3</span>)
-Dstubrunner.idsToServiceIds.fraudDetectionServer=someNameThatShouldMapFraudDetectionServer (<span class="hl-number">4</span>)
(<span class="hl-number">1</span>) - we tell Stub Runner where all the stubs reside
(<span class="hl-number">2</span>) - we don<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'t want the default behaviour where the discovery service is stubbed. That'</span>s why the stub registration will be picked
(<span class="hl-number">3</span>) - we provide a list of stubs to download
(<span class="hl-number">4</span>) - we provide a list of artifactId to serviceId mapping</pre><p>That way your deployed application can send requests to started WireMock servers via the service
discovery. Most likely points 1-3 could be set by default in <code class="literal">application.yml</code> cause they are not
likely to change. That way you can provide only the list of stubs to download whenever you start
the Stub Runner Boot.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stubs_per_consumer" href="#_stubs_per_consumer"></a>2.10&nbsp;Stubs Per Consumer</h2></div></div></div><p>There are cases in which 2 consumers of the same endpoint want to have 2 different responses.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>This approach also allows you to immediately know which consumer is using which part of your API.
You can remove part of a response that your API produces and you can see which of your autogenerated tests
fails. If none fails then you can safely delete that part of the response cause nobody is using it.</p></td></tr></table></div><p>Let&#8217;s look at the following example for contract defined for the producer called <code class="literal">producer</code>.
There are 2 consumers: <code class="literal">foo-consumer</code> and <code class="literal">bar-consumer</code>.</p><p><span class="strong"><strong>Consumer <code class="literal">foo-service</code></strong></span></p><pre class="programlisting">request {
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo'</span>
method GET()
}
response {
status <span class="hl-number">200</span>
body(
foo: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
}
}</pre><p><span class="strong"><strong>Consumer <code class="literal">bar-service</code></strong></span></p><pre class="programlisting">request {
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo'</span>
method GET()
}
response {
status <span class="hl-number">200</span>
body(
bar: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
}
}</pre><p>You can&#8217;t produce for the same request 2 different responses. That&#8217;s why you can properly package the
contracts and then profit from the <code class="literal">stubsPerConsumer</code> feature.</p><p>On the producer side the consumers can have a folder that contains contracts related only to them.
By setting the <code class="literal">stubrunner.stubs-per-consumer</code> flag to <code class="literal">true</code> we no longer register all stubs but only those that
correspond to the consumer application&#8217;s name. In other words we&#8217;ll scan the path of every stub and
if it contains the subfolder with name of the consumer in the path only then will it get registered.</p><p>On the <code class="literal">foo</code> producer side the contracts would look like this</p><pre class="programlisting">.
&#9492;&#9472;&#9472; contracts
&#9500;&#9472;&#9472; bar-consumer
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturnedForBar.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; shouldCallBar.groovy
&#9492;&#9472;&#9472; foo-consumer
&#9500;&#9472;&#9472; bookReturnedForFoo.groovy
&#9492;&#9472;&#9472; shouldCallFoo.groovy</pre><p>Being the <code class="literal">bar-consumer</code> consumer you can either set the <code class="literal">spring.application.name</code> or the <code class="literal">stubrunner.consumer-name</code> to <code class="literal">bar-consumer</code>
Or set the test as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(properties = ["spring.application.name=bar-consumer"])</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
stubsPerConsumer = true)</span></em>
<em><span class="hl-annotation" style="color: gray">@DirtiesContext</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerStubsPerConsumerSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
...
}</pre><p>Then only the stubs registered under a path that contains the <code class="literal">bar-consumer</code> in its name (i.e. those from the
<code class="literal">src/test/resources/contracts/bar-consumer/some/contracts/&#8230;&#8203;</code> folder) will be allowed to be referenced.</p><p>Or set the consumer name explicitly</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
consumerName = "foo-consumer",
stubsPerConsumer = true)</span></em>
<em><span class="hl-annotation" style="color: gray">@DirtiesContext</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerStubsPerConsumerWithConsumerNameSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
...
}</pre><p>Then only the stubs registered under a path that contains the <code class="literal">foo-consumer</code> in its name (i.e. those from the
<code class="literal">src/test/resources/contracts/foo-consumer/some/contracts/&#8230;&#8203;</code> folder) will be allowed to be referenced.</p><p>You can check out <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/issues/224" target="_top">issue 224</a> for more
information about the reasons behind this change.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_common" href="#_common"></a>2.11&nbsp;Common</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_properties_for_junit_and_spring" href="#_common_properties_for_junit_and_spring"></a>2.11.1&nbsp;Common properties for JUnit and Spring</h3></div></div></div><p>Some of the properties that are repetitive can be set using system properties or configuration properties (for Spring). Here are their names with their default values:</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Property name</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Default value</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Description</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.minPort</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Minimal value of a port for a started WireMock with stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.maxPort</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>15000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Minimal value of a port for a started WireMock with stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.repositoryRoot</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maven repo url. If blank then will call the local maven repo</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.classifier</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubs</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default classifier for the stub artifacts</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.workOffline</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If true then will not contact any remote repositories to download stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.ids</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Array of Ivy notation stubs to download</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Optional username to access the tool that stores the JARs with stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Optional password to access the tool that stores the JARs with stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.stubsPerConsumer</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Set to <code class="literal">true</code> if you want to use different stubs per each consumer instead of registering all stubs for every consumer</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.consumerName</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="" align="left" valign="top"><p>If you want to use stubs per consumer and want to override the consumer name just change this value</p></td></tr></tbody></table></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stub_runner_stubs_ids" href="#_stub_runner_stubs_ids"></a>Stub runner stubs ids</h4></div></div></div><p>You can provide the stubs to download via the <code class="literal">stubrunner.ids</code> system property. They follow the following pattern:</p><pre class="programlisting">groupId:artifactId:version:classifier:port</pre><p><code class="literal">version</code>, <code class="literal">classifier</code> and <code class="literal">port</code> are optional.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">If you don&#8217;t provide the <code class="literal">port</code> then a random one will be picked</li><li class="listitem">If you don&#8217;t provide the <code class="literal">classifier</code> then the default one will be taken. (NOTE that you can pass an empty classifier like this <code class="literal">groupId:artifactId:version:</code>)</li><li class="listitem">If you don&#8217;t provide the <code class="literal">version</code> then the <code class="literal">+</code> will be passed and the latest one will be downloaded</li></ul></div><p>Where <code class="literal">port</code> means the port of the WireMock server.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting from version 1.0.4 as a version you can provide a range of versions that you would like
the Stub Runner to take into consideration. You can read more about the <a class="link" href="https://wiki.eclipse.org/Aether/New_and_Noteworthy#Version_Ranges" target="_top">Aether versioning ranges here</a>.</p></td></tr></table></div><p>Taken from <a class="link" href="https://download.eclipse.org/aether/aether-core/0.9.0/apidocs/org/eclipse/aether/util/version/GenericVersionScheme.html" target="_top">Aether Docs</a>:</p><div class="blockquote"><blockquote class="blockquote"><p>This scheme accepts versions of any form, interpreting a version as a sequence of numeric and alphabetic segments. The characters '-', '_', and '.' as well as the mere
transitions from digit to letter and vice versa delimit the version segments. Delimiters are treated as equivalent.</p><p>Numeric segments are compared mathematically, alphabetic segments are compared lexicographically and case-insensitively. However, the following qualifier strings are
recognized and treated specially: "alpha" = "a" &lt; "beta" = "b" &lt; "milestone" = "m" &lt; "cr" = "rc" &lt; "snapshot" &lt; "final" = "ga" &lt; "sp". All of those well-known qualifiers
are considered smaller/older than other strings. An empty segment/string is equivalent to 0.</p><p>In addition to the above mentioned qualifiers, the tokens "min" and "max" may be used as final version segment to denote the smallest/greatest version having a given prefix.
For example, "1.2.min" denotes the smallest version in the 1.2 line, "1.2.max" denotes the greatest version in the 1.2 line. A version range of the form "[M.N.*]" is short for "[M.N.min, M.N.max]".</p><p>Numbers and strings are considered incomparable against each other. Where version segments of different kind would collide, comparison will instead assume that the previous
segments are padded with trailing 0 or "ga" segments, respectively, until the kind mismatch is resolved, e.g. "1-alpha" = "1.0.0-alpha" &lt; "1.0.1-ga" = "1.0.1".</p></blockquote></div></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_for_messaging" href="#_stub_runner_for_messaging"></a>2.12&nbsp;Stub Runner for Messaging</h2></div></div></div><p>Stub Runner has the functionality to run the published stubs in memory. It can integrate with the following frameworks out of the box</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Spring Integration</li><li class="listitem">Spring Cloud Stream</li><li class="listitem">Apache Camel</li><li class="listitem">Spring AMQP</li></ul></div><p>It also provides points of entry to integrate with any other solution on the market.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stub_triggering" href="#_stub_triggering"></a>2.12.1&nbsp;Stub triggering</h3></div></div></div><p>To trigger a message it&#8217;s enough to use the <code class="literal">StubTrigger</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.stubrunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Collection;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Map;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StubTrigger {
<strong class="hl-tag" style="color: blue">/**
* Triggers an event by a given label for a given {@code groupid:artifactid} notation. You can use only {@code artifactId} too.
*
* Feature related to messaging.
*
* @return true - if managed to run a trigger
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> trigger(String ivyNotation, String labelName);
<strong class="hl-tag" style="color: blue">/**
* Triggers an event by a given label.
*
* Feature related to messaging.
*
* @return true - if managed to run a trigger
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> trigger(String labelName);
<strong class="hl-tag" style="color: blue">/**
* Triggers all possible events.
*
* Feature related to messaging.
*
* @return true - if managed to run a trigger
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> trigger();
<strong class="hl-tag" style="color: blue">/**
* Returns a mapping of ivy notation of a dependency to all the labels it has.
*
* Feature related to messaging.
*/</strong>
Map&lt;String, Collection&lt;String&gt;&gt; labels();
}</pre><p>For convenience the <code class="literal">StubFinder</code> interface extends <code class="literal">StubTrigger</code> so it&#8217;s enough to use only one in your tests.</p><p><code class="literal">StubTrigger</code> gives you the following options to trigger a message:</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_trigger_by_label" href="#_trigger_by_label"></a>Trigger by label</h4></div></div></div><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_trigger_by_group_and_artifact_ids" href="#_trigger_by_group_and_artifact_ids"></a>Trigger by group and artifact ids</h5></div></div></div><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:camelService'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_trigger_by_artifact_ids" href="#_trigger_by_artifact_ids"></a>Trigger by artifact ids</h5></div></div></div><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'camelService'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_trigger_all_messages" href="#_trigger_all_messages"></a>Trigger all messages</h4></div></div></div><pre class="programlisting">stubFinder.trigger()</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_camel" href="#_stub_runner_camel"></a>2.13&nbsp;Stub Runner Camel</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module gives you an easy way to integrate with Apache Camel.
For the provided artifacts it will automatically download the stubs and register the required
routes.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_it_to_the_project" href="#_adding_it_to_the_project"></a>2.13.1&nbsp;Adding it to the project</h3></div></div></div><p>It&#8217;s enough to have both Apache Camel and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_examples" href="#_examples"></a>2.13.2&nbsp;Examples</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stubs_structure" href="#_stubs_structure"></a>Stubs structure</h4></div></div></div><p>Let us assume that we have the following Maven repository with a deployed stubs for the
<code class="literal">camelService</code> application.</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; io
&#9492;&#9472;&#9472; codearte
&#9492;&#9472;&#9472; accurest
&#9492;&#9472;&#9472; stubs
&#9492;&#9472;&#9472; camelService
&#9500;&#9472;&#9472; <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; camelService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; camelService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>And the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; repository
&#9500;&#9472;&#9472; accurest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookDeleted.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturned1.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; bookReturned2.groovy
&#9492;&#9472;&#9472; mappings</pre><p>Let&#8217;s consider the following contracts (let' number it with <span class="strong"><strong>1</strong></span>):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>
input {
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>and number <span class="strong"><strong>2</strong></span></p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_2'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_1_no_input_message_2" href="#_scenario_1_no_input_message_2"></a>Scenario 1 (no input message)</h4></div></div></div><p>So as to trigger a message via the <code class="literal">return_book_1</code> label we&#8217;ll use the <code class="literal">StubTigger</code> interface as follows</p><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">jms:output</code></p><pre class="programlisting">Exchange receivedMessage = camelContext.createConsumerTemplate().receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>, <span class="hl-number">5000</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_2_output_triggered_by_input_2" href="#_scenario_2_output_triggered_by_input_2"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">jms:output</code> destination.</p><pre class="programlisting">camelContext.createProducerTemplate().sendBodyAndHeaders(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>])</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">jms:output</code></p><pre class="programlisting">Exchange receivedMessage = camelContext.createConsumerTemplate().receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>, <span class="hl-number">5000</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_3_input_with_no_output" href="#_scenario_3_input_with_no_output"></a>Scenario 3 (input with no output)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">jms:output</code> destination.</p><pre class="programlisting">camelContext.createProducerTemplate().sendBodyAndHeaders(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>])</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_integration" href="#_stub_runner_integration"></a>2.14&nbsp;Stub Runner Integration</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module gives you an easy way to integrate with Spring Integration.
For the provided artifacts it will automatically download the stubs and register the required
routes.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_it_to_the_project_2" href="#_adding_it_to_the_project_2"></a>2.14.1&nbsp;Adding it to the project</h3></div></div></div><p>It&#8217;s enough to have both Spring Integration and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_examples_2" href="#_examples_2"></a>2.14.2&nbsp;Examples</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stubs_structure_2" href="#_stubs_structure_2"></a>Stubs structure</h4></div></div></div><p>Let us assume that we have the following Maven repository with a deployed stubs for the
<code class="literal">integrationService</code> application.</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; io
&#9492;&#9472;&#9472; codearte
&#9492;&#9472;&#9472; accurest
&#9492;&#9472;&#9472; stubs
&#9492;&#9472;&#9472; integrationService
&#9500;&#9472;&#9472; <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; integrationService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; integrationService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>And the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; repository
&#9500;&#9472;&#9472; accurest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookDeleted.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturned1.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; bookReturned2.groovy
&#9492;&#9472;&#9472; mappings</pre><p>Let&#8217;s consider the following contracts (let' number it with <span class="strong"><strong>1</strong></span>):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>
input {
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>and number <span class="strong"><strong>2</strong></span></p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_2'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'input'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>and the following Spring Integration Route:</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;beans:beans</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.springframework.org/schema/integration"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:beans</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.springframework.org/schema/beans"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- REQUIRED FOR TESTING --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;bridge</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">input-channel</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"output"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">output-channel</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"outputTest"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;channel</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">id</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"outputTest"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;queue/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/channel&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/beans:beans&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_1_no_input_message_3" href="#_scenario_1_no_input_message_3"></a>Scenario 1 (no input message)</h4></div></div></div><p>So as to trigger a message via the <code class="literal">return_book_1</code> label we&#8217;ll use the <code class="literal">StubTigger</code> interface as follows</p><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">output</code></p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'outputTest'</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_2_output_triggered_by_input_3" href="#_scenario_2_output_triggered_by_input_3"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">output</code> destination.</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'input'</span>)</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">output</code></p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'outputTest'</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_3_input_with_no_output_2" href="#_scenario_3_input_with_no_output_2"></a>Scenario 3 (input with no output)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">input</code> destination.</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete'</span>)</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_stream" href="#_stub_runner_stream"></a>2.15&nbsp;Stub Runner Stream</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module gives you an easy way to integrate with Spring Stream.
For the provided artifacts it will automatically download the stubs and register the required
routes.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>In Stub Runner&#8217;s integration with Stream the <code class="literal">messageFrom</code> or <code class="literal">sentTo</code> Strings are resolved
first as a <code class="literal">destination</code> of a channel, and then if there is no such <code class="literal">destination</code> it&#8217;s resolved as a
channel name.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you want to use Spring Cloud Stream remember to add a
<code class="literal">org.springframework.cloud:spring-cloud-stream-test-support</code> dependency.</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-stream-test-support"</span></pre><p class="secondary">
</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_it_to_the_project_3" href="#_adding_it_to_the_project_3"></a>2.15.1&nbsp;Adding it to the project</h3></div></div></div><p>It&#8217;s enough to have both Spring Cloud Stream and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_examples_3" href="#_examples_3"></a>2.15.2&nbsp;Examples</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stubs_structure_3" href="#_stubs_structure_3"></a>Stubs structure</h4></div></div></div><p>Let us assume that we have the following Maven repository with a deployed stubs for the
<code class="literal">streamService</code> application.</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; io
&#9492;&#9472;&#9472; codearte
&#9492;&#9472;&#9472; accurest
&#9492;&#9472;&#9472; stubs
&#9492;&#9472;&#9472; streamService
&#9500;&#9472;&#9472; <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; streamService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; streamService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>And the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; repository
&#9500;&#9472;&#9472; accurest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookDeleted.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturned1.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; bookReturned2.groovy
&#9492;&#9472;&#9472; mappings</pre><p>Let&#8217;s consider the following contracts (let' number it with <span class="strong"><strong>1</strong></span>):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>
input { triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>) }
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers { header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>) }
}
}</pre><p>and number <span class="strong"><strong>2</strong></span></p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_2'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookStorage'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders { header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>) }
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers { header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>) }
}
}</pre><p>and the following Spring configuration:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner.repositoryRoot</span>: classpath:m2repo/repository/
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner.ids</span>: org.springframework.cloud.contract.verifier.stubs:streamService:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT:stubs
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> output</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: returnBook
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> input</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: bookStorage
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">0</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">debug</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_1_no_input_message_4" href="#_scenario_1_no_input_message_4"></a>Scenario 1 (no input message)</h4></div></div></div><p>So as to trigger a message via the <code class="literal">return_book_1</code> label we&#8217;ll use the <code class="literal">StubTrigger</code> interface as follows</p><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><p>Next we&#8217;ll want to listen to the output of the message sent to a channel whose <code class="literal">destination</code> is <code class="literal">returnBook</code></p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_2_output_triggered_by_input_4" href="#_scenario_2_output_triggered_by_input_4"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">bookStorage</code> <code class="literal">destination</code>.</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookStorage'</span>)</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">returnBook</code></p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_3_input_with_no_output_3" href="#_scenario_3_input_with_no_output_3"></a>Scenario 3 (input with no output)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">output</code> destination.</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete'</span>)</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_spring_amqp" href="#_stub_runner_spring_amqp"></a>2.16&nbsp;Stub Runner Spring AMQP</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module provides an easy way to integrate with Spring AMQP&#8217;s Rabbit Template.
For the provided artifacts it will automatically download the stubs and register the required
routes.</p><p>The integration tries to work standalone, that is without interaction with a running RabbitMQ message broker.
It expects a <code class="literal">RabbitTemplate</code> on the application context and uses it as a spring boot test <code class="literal">@SpyBean</code>.
Thus it can use the mockito spy functionality to verify and introspect messages sent by the application.</p><p>On the message consumer side, it considers all <code class="literal">@RabbitListener</code> annotated endpoints as well as all `SimpleMessageListenerContainer`s on the application context.</p><p>As messages are usually sent to exchanges in AMQP the message contract contains the exchange name as the destination.
Message listeners on the other side are bound to queues. Bindings connect an exchange to a queue.
If message contracts are triggered the Spring AMQP stub runner integration will look for bindings on the application context that match this exchange.
Then it collects the queues from the Spring exchanges and tries to find messages listeners bound to these queues.
The message is triggered to all matching message listeners.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_it_to_the_project_4" href="#_adding_it_to_the_project_4"></a>2.16.1&nbsp;Adding it to the project</h3></div></div></div><p>It&#8217;s enough to have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and set the property <code class="literal">stubrunner.amqp.enabled=true</code>.
Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_examples_4" href="#_examples_4"></a>2.16.2&nbsp;Examples</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stubs_structure_4" href="#_stubs_structure_4"></a>Stubs structure</h4></div></div></div><p>Let us assume that we have the following Maven repository with a deployed stubs for the
<code class="literal">spring-cloud-contract-amqp-test</code> application.</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; com
&#9492;&#9472;&#9472; example
&#9492;&#9472;&#9472; spring-cloud-contract-amqp-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>
&#9500;&#9472;&#9472; <span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; spring-cloud-contract-amqp-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>-<span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; spring-cloud-contract-amqp-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>-<span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>And the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; contracts
&#9492;&#9472;&#9472; shouldProduceValidPersonData.groovy</pre><p>Let&#8217;s consider the following contract:</p><pre class="programlisting">Contract.make {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Human readable description</span>
description <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Should produce valid person data'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Label by means of which the output message can be triggered</span>
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contract-test.person.created.event'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// input to the contract</span>
input {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the contract will be triggered by a method</span>
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'createPerson()'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// output message of the contract</span>
outputMessage {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// destination to which the output message will be sent</span>
sentTo <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contract-test.exchange'</span>
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contentType'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'__TypeId__'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.stubrunner.messaging.amqp.Person'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the body of the output message</span>
body ([
id: $(consumer(<span class="hl-number">9</span>), producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]+"</span>))),
name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"me"</span>
])
}
}</pre><p>and the following Spring configuration:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositoryRoot</span>: classpath:m2repo/repository/
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ids</span>: org.springframework.cloud.contract.verifier.stubs.amqp:spring-cloud-contract-amqp-test:<span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT:stubs
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> amqp</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">0</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_triggering_the_message" href="#_triggering_the_message"></a>Triggering the message</h4></div></div></div><p>So to trigger a message using the contract above we&#8217;ll use the <code class="literal">StubTrigger</code> interface as follows.</p><pre class="programlisting">stubTrigger.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contract-test.person.created.event"</span>)</pre><p>The message has the destination <code class="literal">contract-test.exchange</code> so the Spring AMQP stub runner integration looks for bindings related to this exchange.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Binding binding() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> BindingBuilder.bind(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test.queue"</span>)).to(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DirectExchange(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contract-test.exchange"</span>)).with(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"#"</span>);
}</pre><p>The binding definition binds the queue <code class="literal">test.queue</code>.
So the following listener definition is a match and is invoked with the contract message.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test.queue"</span>);
container.setMessageListener(listenerAdapter);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> container;
}</pre><p>Also, the following annotated listener represents a match and would be invoked.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "test.queue"),
exchange = @Exchange(value = "contract-test.exchange", ignoreDeclarationExceptions = "true")))</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handlePerson(Person person) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.person = person;
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The message is directly handed over to the <code class="literal">onMessage</code> method of the <code class="literal">MessageListener</code> associated with the matching <code class="literal">SimpleMessageListenerContainer</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_amqp_test_configuration" href="#_spring_amqp_test_configuration"></a>Spring AMQP Test Configuration</h4></div></div></div><p>In order to avoid that Spring AMQP is trying to connect to a running broker during our tests we configure a mock <code class="literal">ConnectionFactory</code>.</p><p>To disable the mocked ConnectionFactory set the property <code class="literal">stubrunner.amqp.mockConnection=false</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> amqp</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> mockConnection</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span></pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_contract_dsl" href="#_contract_dsl"></a>2.17&nbsp;Contract DSL</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember that inside the contract file you have to provide the fully qualified name to
the <code class="literal">Contract</code> class and the <code class="literal">make</code> static import i.e. <code class="literal">org.springframework.cloud.spec.Contract.make { &#8230;&#8203; }</code>.
You can also provide an import to the <code class="literal">Contract</code> class <code class="literal">import org.springframework.cloud.spec.Contract</code> and then call
<code class="literal">Contract.make { &#8230;&#8203; }</code></p></td></tr></table></div><p>Contract DSL is written in Groovy, but don&#8217;t be alarmed if you didn&#8217;t use Groovy before. Knowledge of the language is not really needed as our DSL uses only
a tiny subset of it (namely literals, method calls and closures). What&#8217;s more the DSL is designed to be programmer-readable without any knowledge of the DSL itself -
it&#8217;s statically typed.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Contract supports defining multiple contracts in a single file!</p></td></tr></table></div><p>The Contract is present in the <code class="literal">spring-cloud-contract-spec</code> module of the Spring Cloud Contract Verifier repository.</p><p>Let&#8217;s look at full example of a contract definition.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/api/12'</span>
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/vnd.org.springframework.cloud.contract.verifier.twitter-places-analyzer.v1+json'</span>
}
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'\
</span> [{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"created_at"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Sat Jul 26 09:38:57 +0000 2014"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>: <span class="hl-number">492967299297845248</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id_str"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"492967299297845248"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Gonna see you at Warsaw"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"place"</span>:
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"attributes"</span>:{},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bounding_box"</span>:
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"coordinates"</span>:
[[
[-<span class="hl-number">77.119759</span>,<span class="hl-number">38.791645</span>],
[-<span class="hl-number">76.909393</span>,<span class="hl-number">38.791645</span>],
[-<span class="hl-number">76.909393</span>,<span class="hl-number">38.995548</span>],
[-<span class="hl-number">77.119759</span>,<span class="hl-number">38.995548</span>]
]],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Polygon"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"country"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"United States"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"country_code"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"US"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"full_name"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Washington, DC"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"01fbe706f872cb32"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Washington"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"place_type"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"city"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://api.twitter.com/1/geo/id/01fbe706f872cb32.json"</span>
}
}]
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> }
response {
status <span class="hl-number">200</span>
}
}</pre><p>Not all features of the DSL are used in example above. If you didn&#8217;t find what you are looking for, please check next paragraphs on this page.</p><div class="blockquote"><blockquote class="blockquote"><p>You can easily compile Contracts to WireMock stubs mapping using standalone maven command: <code class="literal">mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert</code>.</p></blockquote></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_limitations_2" href="#_limitations_2"></a>2.17.1&nbsp;Limitations</h3></div></div></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Contract Verifier doesn&#8217;t support XML properly. Please use JSON or help us implement this feature.</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>The support for the verification of size of JSON arrays is experimental. If you want to turn it on please provide
the value of a system property <code class="literal">spring.cloud.contract.verifier.assert.size</code> equal to <code class="literal">true</code>. By default this feature is set to
<code class="literal">false</code>. You can also provide the <code class="literal">assertJsonSize</code> property in the plugin configuration.</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Due to the fact that JSON structure can have any form it&#8217;s sometimes impossible to parse it properly when using
the <code class="literal">value(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code> notation when using that in GString. That&#8217;s why we highly recommend using the
Groovy Map notation.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_top_level_elements" href="#_common_top_level_elements"></a>2.17.2&nbsp;Common Top-Level elements</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_description" href="#_description"></a>Description</h4></div></div></div><p>You can add a <code class="literal">description</code> to your contract that is nothing else but an arbitrary text. Example:</p><pre class="programlisting"> org.springframework.cloud.contract.spec.Contract.make {
description(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span>given:
An input
when:
Sth happens
then:
Output
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">')
</span> }</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_name" href="#_name"></a>Name</h4></div></div></div><p>You can provide a name of your contract. Let&#8217;s assume that you&#8217;ve provided a name <code class="literal">should register a user</code>.
If you do this then the name of the autogenerated test will be equal to <code class="literal">validate_should_register_a_user</code>.
Also the name of the stub will be <code class="literal">should_register_a_user.json</code> in case of a WireMock stub.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Please ensure that the name doesn&#8217;t contain any characters that will make the generated test
not possible to compile. Also remember that if you provide the same name for multiple contracts then your
autogenerated tests will fail to compile and your generated stubs will override each other.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_ignoring_contracts" href="#_ignoring_contracts"></a>Ignoring contracts</h4></div></div></div><p>If you want to ignore a contract you can either set a value of ignored contracts in the plugin configuration
or just set the <code class="literal">ignored</code> property on the contract itself:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
ignored()
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_passing_values_from_files" href="#_passing_values_from_files"></a>Passing values from files</h4></div></div></div><p>Starting with version <code class="literal">1.2.0</code> it&#8217;s possible to pass values from files. Let&#8217;s assume that we have
the following resources in our project.</p><pre class="programlisting">&#9492;&#9472;&#9472; src
&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; resources
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; contracts
&nbsp;&nbsp;&nbsp; &#9500;&#9472;&#9472; readFromFile.groovy
&nbsp;&nbsp;&nbsp; &#9500;&#9472;&#9472; request.json
&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472; response.json</pre><p>And our contract looks like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>)
headers {
contentType(applicationJson())
}
body(file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request.json"</span>))
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/1"</span>)
}
response {
status <span class="hl-number">200</span>
body(file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response.json"</span>))
headers {
contentType(textPlain())
}
}
}</pre><p>and the json files look like this:</p><p><span class="strong"><strong>request.json</strong></span></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"REQUEST"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p><span class="strong"><strong>response.json</strong></span></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"RESPONSE"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>When test / stub generation takes place then the contents of the file will be
passed to the body of request / response. All thanks to the <code class="literal">file(&#8230;&#8203;)</code> method.
The argument of that method needs to be a file with location relative to the
folder in which the contract lays.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_http_top_level_elements" href="#_http_top_level_elements"></a>2.17.3&nbsp;HTTP Top-Level Elements</h3></div></div></div><p>Following methods can be called in the top-level closure of a contract definition. Request and response are mandatory, priority is optional.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Definition of HTTP request part of the contract</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (this can be a valid request or invalid depending</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// on type of contract being specified).</span>
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Definition of HTTP response part of the contract</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (a service implementing this contract should respond</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// with following response after receiving request</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// specified in "request" part above).</span>
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Contract priority, which can be used for overriding</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// contracts (1 is highest). Priority is optional.</span>
priority <span class="hl-number">1</span>
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_request" href="#_request"></a>2.17.4&nbsp;Request</h3></div></div></div><p>HTTP protocol requires only <span class="strong"><strong>method and address</strong></span> to be specified in a request. The same information is mandatory in request definition of the Contract.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// HTTP request method (GET/POST/PUT/DELETE).</span>
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Path component of request URL is specified as follows.</span>
urlPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users'</span>)
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
}</pre><p>It is possible to specify whole <code class="literal">url</code> instead of just path, but <code class="literal">urlPath</code> is the recommended way as it makes the tests <span class="strong"><strong>host-independent</strong></span>.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Specifying `url` and `urlPath` in one contract is illegal.</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://localhost:8888/users'</span>)
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
}</pre><p>Request may contain <span class="strong"><strong>query parameters</strong></span>, which are specified in a closure nested in a call to <code class="literal">urlPath</code> or <code class="literal">url</code>.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
urlPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users'</span>) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Each parameter is specified in form</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `'paramName' : paramValue` where parameter value</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// may be a simple literal or one of matcher functions,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// all of which are used in this example.</span>
queryParameters {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// If a simple literal is used as value</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// default matcher function is used (equalTo)</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'limit'</span>: <span class="hl-number">100</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `equalTo` function simply compares passed value</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// using identity operator (==).</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'filter'</span>: equalTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"email"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `containing` function matches strings</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// that contains passed substring.</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'gender'</span>: value(consumer(containing(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[mf]"</span>)), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'mf'</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `matching` function tests parameter</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// against passed regular expression.</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'offset'</span>: value(consumer(matching(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]+"</span>)), producer(<span class="hl-number">123</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `notMatching` functions tests if parameter</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// does not match passed regular expression.</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loginStartsWith'</span>: value(consumer(notMatching(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".{0,2}"</span>)), producer(<span class="hl-number">3</span>))
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
}</pre><p>It may contain additional <span class="strong"><strong>request headers</strong></span>&#8230;&#8203;</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Each header is added in form `'Header-Name' : 'Header-Value'`.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// there are also some helper methods</span>
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'key'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'value'</span>
contentType(applicationJson())
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
}</pre><p>&#8230;&#8203;and a <span class="strong"><strong>request body</strong></span>.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Currently only JSON format of request body is supported.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Format will be determined from a header or body's content.</span>
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "login" : "john", "name": "John The Contract" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
}</pre><p>Request may contain <span class="strong"><strong>multipart</strong></span> elements. Just call the <code class="literal">multipart()</code> method.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/multipart"</span>
headers {
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'multipart/form-data;boundary=AaB03x'</span>)
}
multipart(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// key (parameter name), value (parameter value) pair</span>
formParameter: $(c(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'".+"'</span>)), p(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'"formParameterValue"'</span>)),
someBooleanParameter: $(c(regex(anyBoolean())), p(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'true'</span>)),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// a named parameter (e.g. with `file` name) that represents file with</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `name` and `content`. You can also call `named("fileName", "fileContent")`</span>
file: named(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// name of the file</span>
name: $(c(regex(nonEmpty())), p(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'filename.csv'</span>)),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// content of the file</span>
content: $(c(regex(nonEmpty())), p(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'file content'</span>)))
)
}
response {
status <span class="hl-number">200</span>
}
}</pre><p>In this example we defined parameters either directly by using the map notation,
where the value can be a dynamic property (e.g. <code class="literal">formParameter: $(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code>)
or by using the <code class="literal">named(&#8230;&#8203;)</code> method that allows you to set a named parameter.
A named parameter can set a <code class="literal">name</code> and <code class="literal">content</code>. You can call it either via
a method with 2 arguments: e.g. <code class="literal">named("fileName", "fileContent")</code> or
via a map notation <code class="literal">named(name: "fileName", content: "fileContent")</code>.</p><p>From this contract the generated test will look more or less like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"multipart/form-data;boundary=AaB03x"</span>)
.param(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"formParameter"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\"formParameterValue\""</span>)
.param(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"someBooleanParameter"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"true"</span>)
.multiPart(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"file"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"filename.csv"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"file content"</span>.getBytes());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/multipart"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);</pre><p>The WireMock stub will look more or less like this:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/multipart"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"multipart/form-data;boundary=AaB03x.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"</span>formParameter\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n\\"</span>.+\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n--\\\\1.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"</span>someBooleanParameter\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n(true|false)\\r\\n--\\\\1.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"</span>file\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"; filename=\\"</span>.+\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n.+\\r\\n--\\\\1.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transformers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response-template"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_response" href="#_response"></a>2.17.5&nbsp;Response</h3></div></div></div><p>Minimal response must contain <span class="strong"><strong>HTTP status code</strong></span>.</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Status code sent by the server</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in response to request specified above.</span>
status <span class="hl-number">200</span>
}
}</pre><p>Besides status response may contain <span class="strong"><strong>headers</strong></span> and <span class="strong"><strong>body</strong></span>, which are specified the same way as in the request (see previous paragraph).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_dynamic_properties" href="#_dynamic_properties"></a>2.17.6&nbsp;Dynamic properties</h3></div></div></div><p>The contract can contain some dynamic properties - timestamps / ids etc. You don&#8217;t want to enforce the consumers to stub their
clocks to always return the same value of time so that it gets matched by the stub. That&#8217;s why we allow you to provide the dynamic
parts in your contracts in two ways. One is to pass them directly in the
body and one to set them in a separate section called <code class="literal">testMatchers</code> and <code class="literal">stubMatchers</code>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_dynamic_properties_inside_the_body" href="#_dynamic_properties_inside_the_body"></a>Dynamic properties inside the body</h4></div></div></div><p>You can set the properties inside the body either via the <code class="literal">value</code> method</p><pre class="programlisting">value(consumer(...), producer(...))
value(c(...), p(...))
value(stub(...), test(...))
value(client(...), server(...))</pre><p>or if you&#8217;re using the Groovy map notation for body you can use the <code class="literal">$()</code> method</p><pre class="programlisting">$(consumer(...), producer(...))
$(c(...), p(...))
$(stub(...), test(...))
$(client(...), server(...))</pre><p>All of the aforementioned approaches are equal. That means that <code class="literal">stub</code> and <code class="literal">client</code> methods are aliases over the <code class="literal">consumer</code>
method. Let&#8217;s take a closer look at what we can do with those values in the subsequent sections.</p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_regular_expressions" href="#_regular_expressions"></a>Regular expressions</h5></div></div></div><p>You can use regular expressions to write your requests in Contract DSL. It is particularly useful when you want to indicate that a given response
should be provided for requests that follow a given pattern. Also, you can use it when you need to use patterns and not exact values both
for your test and your server side tests.</p><p>Please see the example below:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>)
url $(consumer(~/\/[<span class="hl-number">0</span>-<span class="hl-number">9</span>]{<span class="hl-number">2</span>}/), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/12'</span>))
}
response {
status <span class="hl-number">200</span>
body(
id: $(anyNumber()),
surname: $(
consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Kowalsky'</span>),
producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-zA-Z]+'</span>))
),
name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Jan'</span>,
created: $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'2014-02-02 12:23:43'</span>), producer(execute(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'currentDate(it)'</span>))),
correlationId: value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'5d1f9fef-e0dc-4f3d-a7e4-72d2220dd827'</span>),
producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'</span>))
)
)
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'text/plain'</span>
}
}
}</pre><p>You can also provide only one side of the communication using a regular expression. If you do that then automatically we&#8217;ll
provide the generated string that matches the provided regular expression. For example:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>
url value(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo/[0-9]{5}'</span>)))
body([
requestElement: $(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{5}'</span>)))
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>, $(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application\\/vnd\\.fraud\\.v1\\+json;.*'</span>))))
}
}
response {
status <span class="hl-number">200</span>
body([
responseElement: $(producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{7}'</span>)))
])
headers {
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
}
}
}</pre><p>In this example for request and response the opposite side of the communication will have the respective data generated.</p><p>Spring Cloud Contract comes with a series of predefined regular expressions that you can use in your contracts.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern NUMBER = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'-?\\d*(\\.\\d+)?'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern IP_ADDRESS = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern HOSTNAME_PATTERN = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'((http[s]?|ftp):/)/?([^:/\\s]+)(:[0-9]{1,5})?'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern EMAIL = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern URL = UrlHelper.URL
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern UUID = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ANY_DATE = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ANY_DATE_TIME = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ANY_TIME = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern NON_EMPTY = Pattern.compile(/.+/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern NON_BLANK = Pattern.compile(/.*(\S+|\R).*|!^\R*$/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ISO8601_WITH_OFFSET = Pattern.compile(/([<span class="hl-number">0</span>-<span class="hl-number">9</span>]{<span class="hl-number">4</span>})-(<span class="hl-number">1</span>[<span class="hl-number">0</span>-<span class="hl-number">2</span>]|<span class="hl-number">0</span>[<span class="hl-number">1</span>-<span class="hl-number">9</span>])-(<span class="hl-number">3</span>[<span class="hl-number">01</span>]|<span class="hl-number">0</span>[<span class="hl-number">1</span>-<span class="hl-number">9</span>]|[<span class="hl-number">12</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>])T(<span class="hl-number">2</span>[<span class="hl-number">0</span>-<span class="hl-number">3</span>]|[<span class="hl-number">01</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>]):([<span class="hl-number">0</span>-<span class="hl-number">5</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>]):([<span class="hl-number">0</span>-<span class="hl-number">5</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>])(\.\d{<span class="hl-number">3</span>})?(Z|[+-][<span class="hl-number">01</span>]\d:[<span class="hl-number">0</span>-<span class="hl-number">5</span>]\d)/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern anyOf(String... values){
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(values.collect({<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"^$it\$"</span>}).join(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"|"</span>))
}
String onlyAlphaUnicode() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ONLY_ALPHA_UNICODE.pattern()
}
String number() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> NUMBER.pattern()
}
String anyBoolean() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> TRUE_OR_FALSE.pattern()
}
String ipAddress() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> IP_ADDRESS.pattern()
}
String hostname() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HOSTNAME_PATTERN.pattern()
}
String email() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> EMAIL.pattern()
}
String url() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> URL.pattern()
}
String uuid(){
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> UUID.pattern()
}
String isoDate() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ANY_DATE.pattern()
}
String isoDateTime() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ANY_DATE_TIME.pattern()
}
String isoTime() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ANY_TIME.pattern()
}
String iso8601WithOffset() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ISO8601_WITH_OFFSET.pattern()
}
String nonEmpty() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> NON_EMPTY.pattern()
}
String nonBlank() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> NON_BLANK.pattern()
}</pre><p>so in your contract you can use it like this</p><pre class="programlisting">Contract dslWithOptionalsInString = Contract.make {
priority <span class="hl-number">1</span>
request {
method POST()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/password'</span>
headers {
contentType(applicationJson())
}
body(
email: $(consumer(optional(regex(email()))), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'abc@abc.com'</span>)),
callback_url: $(consumer(regex(hostname())), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://partners.com'</span>))
)
}
response {
status <span class="hl-number">404</span>
headers {
contentType(applicationJson())
}
body(
code: value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"123123"</span>), producer(optional(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"123123"</span>))),
message: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"User not found by email = [${value(producer(regex(email())), consumer('not.existing@user.com'))}]"</span>
)
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_passing_optional_parameters" href="#_passing_optional_parameters"></a>Passing optional parameters</h5></div></div></div><p>It is possible to provide optional parameters in your contract. It&#8217;s only possible to have optional parameter for the:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="emphasis"><em>STUB</em></span> side of the Request</li><li class="listitem"><span class="emphasis"><em>TEST</em></span> side of the Response</li></ul></div><p>Example:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
priority <span class="hl-number">1</span>
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/password'</span>
headers {
contentType(applicationJson())
}
body(
email: $(consumer(optional(regex(email()))), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'abc@abc.com'</span>)),
callback_url: $(consumer(regex(hostname())), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://partners.com'</span>))
)
}
response {
status <span class="hl-number">404</span>
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
}
body(
code: value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"123123"</span>), producer(optional(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"123123"</span>)))
)
}
}</pre><p>By wrapping a part of the body with the <code class="literal">optional()</code> method you are in fact creating a regular expression that should be present 0 or more times.</p><p>That way for the example above the following test would be generated if you pick Spock:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span> given:
def request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{"email":"abc@abc.com","callback_url":"http://partners.com"}'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
when:
def response = given().spec(request)
.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/password"</span>)
then:
response.statusCode == <span class="hl-number">404</span>
response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
and:
DocumentContext parsedJson = JsonPath.parse(response.body.asString())
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['code']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(123123)?"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"</span></pre><p>and the following stub:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span>{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/password"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : [ {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]"</span>
}, {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"</span>
} ],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>
}
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">404</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>code\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">123123</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>message\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>User not found by email == [not.existing<em><span class="hl-annotation" style="color: gray">@user.com]\\"}",</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"priority"</span> : <span class="hl-number">1</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_executing_custom_methods_on_server_side" href="#_executing_custom_methods_on_server_side"></a>Executing custom methods on server side</h5></div></div></div><p>It is also possible to define a method call to be executed on the server side during the test. Such a method can be added to the class defined as "baseClassForTests"
in the configuration. Example:</p><p><span class="strong"><strong>Contract</strong></span></p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>
url $(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'^/api/[0-9]{2}$'</span>)), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/api/12'</span>))
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
}
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'\
</span> [{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Gonna see you at Warsaw"</span>
}]
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> }
response {
body (
path: $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/api/12'</span>), producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'^/api/[0-9]{2}$'</span>))),
correlationId: $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'1223456'</span>), producer(execute(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'isProperCorrelationId($it)'</span>)))
)
status <span class="hl-number">200</span>
}
}</pre><p><span class="strong"><strong>Base class</strong></span></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BaseMockMvcSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PairIdController())
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isProperCorrelationId(Integer correlationId) {
assert correlationId == <span class="hl-number">123456</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isEmpty(String value) {
assert value == null
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You can&#8217;t use both a String and <code class="literal">execute</code> to perform concatenation. E.g. calling
<code class="literal">header('Authorization', 'Bearer ' + execute('authToken()'))</code> will lead to improper results.
To make this work just call <code class="literal">header('Authorization', execute('authToken()'))</code> and ensure that
the <code class="literal">authToken()</code> method returns everything that you need.</p></td></tr></table></div><p>The type of the object read from the JSON can be one of the followings depending on the
JSON path:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">String</code> if you point to a <code class="literal">String</code> value in a JSON</li><li class="listitem"><code class="literal">JSONArray</code> if you point to a <code class="literal">List</code> in a JSON</li><li class="listitem"><code class="literal">Map</code> if you point to a <code class="literal">Map</code> in a JSON</li><li class="listitem">proper <code class="literal">Number</code> if you point to <code class="literal">Integer</code>, <code class="literal">Double</code> etc. in a JSON</li><li class="listitem"><code class="literal">Boolean</code> if you point to a <code class="literal">Boolean</code> in a JSON</li></ul></div><p>In the request part of the contract you can specify that the <code class="literal">body</code> should be
taken from a method.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to provide both the consumer and the producer side
and the <code class="literal">execute</code> part can be applied for the whole body. Not for parts of it!</p></td></tr></table></div><p>Example:</p><pre class="programlisting">Contract contractDsl = Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/something'</span>
body(
$(c(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>), p(execute(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"hashCode()"</span>)))
)
}
response {
status <span class="hl-number">200</span>
}
}</pre><p>This will result in calling the <code class="literal">hashCode()</code> method in the request body.
It would more or less like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.body(hashCode());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/something"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);</pre></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_referencing_request_from_response" href="#_referencing_request_from_response"></a>Referencing request from response</h4></div></div></div><p>The best situation is to provide fixed values but sometimes you need to reference a request in your response.
In order to do this you can profit from the <code class="literal">fromRequest()</code> method that allows you to reference a bunch
of elements from the HTTP request. You can use the following options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">fromRequest().url()</code> - return the request URL</li><li class="listitem"><code class="literal">fromRequest().query(String key)</code> - return the first query parameter with a given name</li><li class="listitem"><code class="literal">fromRequest().query(String key, int index)</code> - return the nth query parameter with a given name</li><li class="listitem"><code class="literal">fromRequest().header(String key)</code> - return the first header with a given name</li><li class="listitem"><code class="literal">fromRequest().header(String key, int index)</code> - return the nth header with a given name</li><li class="listitem"><code class="literal">fromRequest().body()</code> - return the full request body</li><li class="listitem"><code class="literal">fromRequest().body(String jsonPath)</code> - return the element from the request that matches the JSON Path</li></ul></div><p>Let&#8217;s take a look at the following contract</p><pre class="programlisting">Contract contractDsl = Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/api/v1/xxxx'</span>) {
queryParameters {
parameter(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>)
parameter(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>)
}
}
headers {
header(authorization(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span>)
header(authorization(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>)
}
body(foo: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>, baz: <span class="hl-number">5</span>)
}
response {
status <span class="hl-number">200</span>
headers {
header(authorization(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo ${fromRequest().header(authorization())} bar"</span>)
}
body(
url: fromRequest().url(),
param: fromRequest().query(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>),
paramIndex: fromRequest().query(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, <span class="hl-number">1</span>),
authorization: fromRequest().header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>),
authorization2: fromRequest().header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>, <span class="hl-number">1</span>),
fullBody: fromRequest().body(),
responseFoo: fromRequest().body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.foo'</span>),
responseBaz: fromRequest().body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.baz'</span>),
responseBaz2: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Bla bla ${fromRequest().body('$.foo')} bla bla"</span>
)
}
}</pre><p>Running a JUnit test generation will lead in creation of a test looking more or less like this</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span>)
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\":\"bar\",\"baz\":5}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>)
.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo secret bar"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fullBody"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\":\"bar\",\"baz\":5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"paramIndex"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseFoo"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization2"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseBaz"</span>).isEqualTo(<span class="hl-number">5</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseBaz2"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Bla bla bar bla bla"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"param"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span>);</pre><p>As you can see elements from the request have been properly referenced in the response.</p><p>The generated WireMock stub will look more or less like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"urlPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"queryParameters"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.baz == 5)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.foo == 'bar')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"url\":\"{{{request.url}}}\",\"param\":\"{{{request.query.foo.[0]}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\",\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\"}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{{{request.headers.Authorization.[0]}}}"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transformers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response-template"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>So sending a request as the one presented in the <code class="literal">request</code> part of the contract will lead in sending the following
response body</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx?foo=bar&amp;foo=bar2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"param"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"paramIndex"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization2"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fullBody"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\":\"bar\",\"baz\":5}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseFoo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseBaz"</span> : <span class="hl-number">5</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseBaz2"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Bla bla bar bla bla"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This feature will work only with WireMock having version greater or equal to 2.5.1. We&#8217;re using WireMock&#8217;s
<code class="literal">response-template</code> response transformer. It&#8217;s using Handlebars to convert the Mustache <code class="literal">{{{ }}}</code> templates into
proper values. Additionally we&#8217;re registering 2 helper functions. <code class="literal">escapejsonbody</code> - that escapes the request
body in a format that can be embedded in a JSON. Another is <code class="literal">jsonpath</code> that for a given parameter knows how to
find an object in the request body.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_dynamic_properties_in_matchers_sections" href="#_dynamic_properties_in_matchers_sections"></a>Dynamic properties in matchers sections</h4></div></div></div><p>If you&#8217;ve been working with <a class="link" href="https://docs.pact.io/" target="_top">Pact</a> this might seem familiar. Quite a few users
are used to having a separation between the body and setting dynamic parts of your contract.</p><p>That&#8217;s why you can profit from two separate sections. One is called <code class="literal">stubMatchers</code> where you can
define the dynamic values that should end up in a stub. You can set it in the <code class="literal">request</code> or <code class="literal">inputMessage</code>
part of your contract. The other is called <code class="literal">testMatchers</code> which is present in the <code class="literal">response</code> or
<code class="literal">outputMessage</code> side of the contract.</p><p>Currently we support only JSON Path based matchers with the following matching possibilities.
For <code class="literal">stubMatchers</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">byEquality()</code> - the value taken from the response via the provided JSON Path needs
to be equal to the provided value in the contract</li><li class="listitem"><code class="literal">byRegex(&#8230;&#8203;)</code> - the value taken from the response via the provided JSON Path needs
to match the regex</li><li class="listitem"><code class="literal">byDate()</code> - the value taken from the response via the provided JSON Path needs to
match the regex for ISO Date</li><li class="listitem"><code class="literal">byTimestamp()</code> - the value taken from the response via the provided JSON Path needs
to match the regex for ISO DateTime</li><li class="listitem"><code class="literal">byTime()</code> - the value taken from the response via the provided JSON Path needs to
match the regex for ISO Time</li></ul></div><p>For <code class="literal">testMatchers</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">byEquality()</code> - the value taken from the response via the provided JSON Path needs
to be equal to the provided value in the contract</li><li class="listitem"><code class="literal">byRegex(&#8230;&#8203;)</code> - the value taken from the response via the provided JSON Path needs
to match the regex</li><li class="listitem"><code class="literal">byDate()</code> - the value taken from the response via the provided JSON Path needs to
match the regex for ISO Date</li><li class="listitem"><code class="literal">byTimestamp()</code> - the value taken from the response via the provided JSON Path needs
to match the regex for ISO DateTime</li><li class="listitem"><code class="literal">byTime()</code> - the value taken from the response via the provided JSON Path needs to
match the regex for ISO Time</li><li class="listitem"><code class="literal">byType()</code> - the value taken from the response via the provided JSON Path needs to
be of the same type as the type defined in the body of the response in the contract.
<code class="literal">byType</code> can take a closure where you can set <code class="literal">minOccurrence</code> and <code class="literal">maxOccurrence</code>.
That way you can assert on the size of the flattened collection. To check the size
of an unflattened collection, use a custom method via <code class="literal">byCommand(&#8230;&#8203;)</code> testMatcher.</li><li class="listitem"><p class="simpara"><code class="literal">byCommand(&#8230;&#8203;)</code> - the value taken from the response via the provided JSON Path will be
passed as an input to the custom method that you&#8217;re providing. E.g. <code class="literal">byCommand('foo($it)')</code>
will result in calling a <code class="literal">foo</code> method to which the value matching the JSON Path will get
passed.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p class="simpara">The type of the object read from the JSON can be one of the followings depending on the
JSON path:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: square; "><li class="listitem"><code class="literal">String</code> if you point to a <code class="literal">String</code> value in a JSON</li><li class="listitem"><code class="literal">JSONArray</code> if you point to a <code class="literal">List</code> in a JSON</li><li class="listitem"><code class="literal">Map</code> if you point to a <code class="literal">Map</code> in a JSON</li><li class="listitem">proper <code class="literal">Number</code> if you point to <code class="literal">Integer</code>, <code class="literal">Double</code> etc. in a JSON</li><li class="listitem"><code class="literal">Boolean</code> if you point to a <code class="literal">Boolean</code> in a JSON</li></ul></div></li></ul></div></li></ul></div><p>Let&#8217;s take a look at the following example:</p><pre class="programlisting">Contract contractDsl = Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
urlPath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/get'</span>
body([
duck: <span class="hl-number">123</span>,
alpha: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc"</span>,
number: <span class="hl-number">123</span>,
aBoolean: true,
date: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2017-01-01"</span>,
dateTime: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2017-01-01T01:23:45"</span>,
time: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"01:02:34"</span>,
valueWithoutAMatcher: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,
valueWithTypeMatch: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"string"</span>,
key: [
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'complex.key'</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
]
])
stubMatchers {
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{3}"</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byRegex(onlyAlphaUnicode()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.number'</span>, byRegex(number()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.aBoolean'</span>, byRegex(anyBoolean()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.date'</span>, byDate())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.dateTime'</span>, byTimestamp())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.time'</span>, byTime())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.['key'].['complex.key']"</span>, byEquality())
}
headers {
contentType(applicationJson())
}
}
response {
status <span class="hl-number">200</span>
body([
duck: <span class="hl-number">123</span>,
alpha: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc"</span>,
number: <span class="hl-number">123</span>,
aBoolean: true,
date: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2017-01-01"</span>,
dateTime: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2017-01-01T01:23:45"</span>,
time: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"01:02:34"</span>,
valueWithoutAMatcher: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,
valueWithTypeMatch: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"string"</span>,
valueWithMin: [
<span class="hl-number">1</span>,<span class="hl-number">2</span>,<span class="hl-number">3</span>
],
valueWithMax: [
<span class="hl-number">1</span>,<span class="hl-number">2</span>,<span class="hl-number">3</span>
],
valueWithMinMax: [
<span class="hl-number">1</span>,<span class="hl-number">2</span>,<span class="hl-number">3</span>
],
valueWithMinEmpty: [],
valueWithMaxEmpty: [],
key: [
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'complex.key'</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
]
])
testMatchers {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts the jsonpath value against manual regex</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{3}"</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts the jsonpath value against the provided value</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byEquality())
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts the jsonpath value against some default regex</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byRegex(onlyAlphaUnicode()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.number'</span>, byRegex(number()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.aBoolean'</span>, byRegex(anyBoolean()))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts vs inbuilt time related regex</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.date'</span>, byDate())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.dateTime'</span>, byTimestamp())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.time'</span>, byTime())
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts that the resulting type is the same as in response body</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithTypeMatch'</span>, byType())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMin'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (min 1)</span>
minOccurrence(<span class="hl-number">1</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMax'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (max 3)</span>
maxOccurrence(<span class="hl-number">3</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMinMax'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (min 1 &amp; max 3)</span>
minOccurrence(<span class="hl-number">1</span>)
maxOccurrence(<span class="hl-number">3</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMinEmpty'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (min 0)</span>
minOccurrence(<span class="hl-number">0</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMaxEmpty'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (max 0)</span>
maxOccurrence(<span class="hl-number">0</span>)
})
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// will execute a method `assertThatValueIsANumber`</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byCommand(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'assertThatValueIsANumber($it)'</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.['key'].['complex.key']"</span>, byEquality())
}
headers {
contentType(applicationJson())
}
}
}</pre><p>In this example we&#8217;re providing the dynamic portions of the contract in the matchers sections.
For the request part you can see that for all fields but <code class="literal">valueWithoutAMatcher</code> we&#8217;re setting
explicitly the values of regular expressions we&#8217;d like the stub to contain. For the <code class="literal">valueWithoutAMatcher</code>
the verification will take place in the same way as without the usage of matchers - the test
will perform an equality check in this case.</p><p>For the response side in the <code class="literal">testMatchers</code> section we&#8217;re defining all the dynamic parts
in a similar manner. The only difference is that we have the <code class="literal">byType</code> matchers too. In that
case we&#8217;re checking 4 fields in the way that we&#8217;re verifying whether the response from the test
has a value whose JSON path matching the given field is of the same type as the one defined in the response body and:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">for <code class="literal">$.valueWithTypeMatch</code> - we&#8217;re just checking the whether the type is the same</li><li class="listitem">for <code class="literal">$.valueWithMin</code> - we&#8217;re checking the type and assert if the size is greater or equal to the min occurrence</li><li class="listitem">for <code class="literal">$.valueWithMax</code> - we&#8217;re checking the type and assert if the size is smaller or equal to the max occurrence</li><li class="listitem">for <code class="literal">$.valueWithMinMax</code> - we&#8217;re checking the type and assert if the size is between the min and max occurrence</li></ul></div><p>The resulting test would look more or less like this (note that we&#8217;re separating the autogenerated
assertions and the one from matchers with an <code class="literal">and</code> section):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"duck\":123,\"alpha\":\"abc\",\"number\":123,\"aBoolean\":true,\"date\":\"2017-01-01\",\"dateTime\":\"2017-01-01T01:23:45\",\"time\":\"01:02:34\",\"valueWithoutAMatcher\":\"foo\",\"valueWithTypeMatch\":\"string\"}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/get"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"valueWithoutAMatcher"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.duck"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{3}"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.duck"</span>, Integer.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).isEqualTo(<span class="hl-number">123</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.alpha"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[\\p{L}]*"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.alpha"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.number"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"-?\\d*(\\.\\d+)?"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.aBoolean"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(true|false)"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.date"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.dateTime"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.time"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithTypeMatch"</span>)).isInstanceOf(java.lang.String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMin"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMin"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).hasSizeGreaterThanOrEqualTo(<span class="hl-number">1</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMax"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMax"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).hasSizeLessThanOrEqualTo(<span class="hl-number">3</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinMax"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinMax"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).hasSizeBetween(<span class="hl-number">1</span>, <span class="hl-number">3</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinEmpty"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinEmpty"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).hasSizeGreaterThanOrEqualTo(<span class="hl-number">0</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMaxEmpty"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMaxEmpty"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).hasSizeLessThanOrEqualTo(<span class="hl-number">0</span>);
assertThatValueIsANumber(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.duck"</span>));</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Notice that for the <code class="literal">byCommand</code> method we are calling the <code class="literal">assertThatValueIsANumber</code>. This method needs
to be defined in the test base class or should be statically imported to your tests.
Notice that the <code class="literal">byCommand</code> call was converted to <code class="literal">assertThatValueIsANumber(parsedJson.read("$.duck"));</code>. That means
that we took the method name and passed the proper JSON path as a parameter to it.</p></td></tr></table></div><p>and the WireMock stub like this:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"urlPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/get"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['valueWithoutAMatcher'] == 'foo')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['valueWithTypeMatch'] == 'string')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.duck =~ /([0-9]{3})/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.duck == 123)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.alpha =~ /([\\\\p{L}]*)/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.alpha == 'abc')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.number =~ /(-?\\\\d*(\\\\.\\\\d+)?)/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.aBoolean =~ /((true|false))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.list.some.nested[?(@.json =~ /(.*)/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>duck\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":123,\\"</span>alpha\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>abc\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>number\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":123,\\"</span>aBoolean\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":true,\\"</span>date\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">01</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>dateTime\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">01</span>T01:<span class="hl-number">23</span>:<span class="hl-number">45</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>time\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">01</span>:<span class="hl-number">02</span>:<span class="hl-number">34</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>valueWithoutAMatcher\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>foo\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>valueWithTypeMatch\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>string\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>valueWithMin\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":[1,2,3],\\"</span>valueWithMax\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":[1,2,3],\\"</span>valueWithMinMax\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":[1,2,3]}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you use a <code class="literal">matcher</code> then the part of the request / response that the <code class="literal">matcher</code> is addressing
via the JSON Path will get removed from assertion. In case of verifying a collection you have to create
matchers for <span class="strong"><strong>all</strong></span> elements of the collection.</p></td></tr></table></div><p>Let&#8217;s look at the following example:</p><pre class="programlisting">Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>)
}
response {
status <span class="hl-number">200</span>
body(events: [[
operation : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'EXPORT'</span>,
eventId : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'16f1ed75-0bcc-4f0d-a04d-3121798faf99'</span>,
status : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'OK'</span>
], [
operation : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'INPUT_PROCESSING'</span>,
eventId : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'3bb4ac82-6652-462f-b6d1-75e424a0024a'</span>,
status : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'OK'</span>
]
]
)
testMatchers {
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.events[0].operation'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.+'</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.events[0].eventId'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.events[0].status'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.+'</span>))
}
}
}</pre><p>This will lead in creating the following test (showing just the assertion section)</p><pre class="programlisting">and:
DocumentContext parsedJson = JsonPath.parse(response.body.asString())
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['eventId']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"16f1ed75-0bcc-4f0d-a04d-3121798faf99"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['operation']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"EXPORT"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['operation']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"INPUT_PROCESSING"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['eventId']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"3bb4ac82-6652-462f-b6d1-75e424a0024a"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['status']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>)
and:
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.events[0].operation"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".+"</span>)
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.events[0].eventId"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})\$"</span>)
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.events[0].status"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".+"</span>)</pre><p>As you can see the assertion is malformed. That&#8217;s because only the first element of the array got asserted.
In order to fix this it&#8217;s best to apply the assertion to the whole <code class="literal">$.events</code> collection and assert it
via the <code class="literal">byCommand(&#8230;&#8203;)</code> method.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_jax_rs_support" href="#_jax_rs_support"></a>2.17.7&nbsp;JAX-RS support</h3></div></div></div><p>We support JAX-RS 2 Client API. Base class needs to define <code class="literal">protected WebTarget webTarget</code> and server initialization, right now the only option how to test JAX-RS API is to start a web server.</p><p>Request with a body needs to have a content type set otherwise <code class="literal">application/octet-stream</code> is going to be used.</p><p>In order to use JAX-RS mode, use the following settings:</p><pre class="programlisting">testMode === <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'JAXRSCLIENT'</span></pre><p>Example of a test API generated:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
Response response = webTarget
.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"limit"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"10"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"offset"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"20"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"filter"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"email"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sort"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"search"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"55"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"age"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"99"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Denis.Stepanov"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"email"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bob@email.com"</span>)
.request()
.method(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>);
String responseAsString = response.readEntity(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.getStatus()).isEqualTo(<span class="hl-number">200</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(responseAsString);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['property1']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_async_support" href="#_async_support"></a>2.17.8&nbsp;Async support</h3></div></div></div><p>If you&#8217;re using asynchronous communication on the server side (your controllers are returning
<code class="literal">Callable</code>, <code class="literal">DeferredResult</code> etc. then inside your contract you have to provide in the <code class="literal">response</code>
section a <code class="literal">async()</code> method. Example:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/get'</span>
}
response {
status <span class="hl-number">200</span>
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Passed'</span>
async()
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_working_with_context_paths" href="#_working_with_context_paths"></a>2.17.9&nbsp;Working with Context Paths</h3></div></div></div><p>Spring Cloud Contract supports context paths.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The only thing that changes in order to fully support context paths is the switch
on the <span class="strong"><strong>PRODUCER</strong></span> side. The autogenerated tests need to be using the <span class="strong"><strong>EXPLICIT</strong></span> mode.</p></td></tr></table></div><p>The consumer side remains untouched, in order for the generated test to pass you have to switch the <span class="strong"><strong>EXPLICIT</strong></span> mode.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testMode&gt;</span>EXPLICIT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">contracts {
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'EXPLICIT'</span>
}</pre><p class="secondary">
</p><p>That way you&#8217;ll generate a test that <span class="strong"><strong>DOES NOT</strong></span> use MockMvc. It means that you&#8217;re generating
real requests and you need to setup your generated test&#8217;s base class to work on a real socket.</p><p>Let&#8217;s imagine the following contract:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/my-context-path/url'</span>
}
response {
status <span class="hl-number">200</span>
}
}</pre><p>Here is an example of how to set up a base class and Rest Assured for everything to work correctly.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.RestAssured;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.web.server.LocalServerPort;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.test.context.SpringBootTest;
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(classes = ContextPathTestingBaseClass.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ContextPathTestingBaseClass {
<em><span class="hl-annotation" style="color: gray">@LocalServerPort</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port;
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssured.baseURI = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost"</span>;
RestAssured.port = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port;
}
}</pre><p>That way all:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">all your requests in the autogenerated tests will be sent to the real endpoint with your context path included (e.g. <code class="literal">/my-context-path/url</code>)</li><li class="listitem">your contracts reflect that you have a context path, thus your generated stubs will also
have that information (e.g. in the stubs you&#8217;ll see that you have too call <code class="literal">/my-context-path/url</code>)</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_messaging_top_level_elements" href="#_messaging_top_level_elements"></a>2.17.10&nbsp;Messaging Top-Level Elements</h3></div></div></div><p>The DSL for messaging looks a little bit different than the one that focuses on HTTP.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_output_triggered_by_a_method" href="#_output_triggered_by_a_method"></a>Output triggered by a method</h4></div></div></div><p>The output message can be triggered by calling a method (e.g. a Scheduler was started and a message was sent)</p><pre class="programlisting">def dsl = Contract.make {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Human readable description</span>
description <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Some description'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Label by means of which the output message can be triggered</span>
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// input to the contract</span>
input {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the contract will be triggered by a method</span>
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// output message of the contract</span>
outputMessage {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// destination to which the output message will be sent</span>
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the body of the output message</span>
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the headers of the output message</span>
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>In this case the output message will be sent to <code class="literal">output</code> if a method called <code class="literal">bookReturnedTriggered</code> will be executed. In the message <span class="strong"><strong>publisher&#8217;s</strong></span> side
we will generate a test that will call that method to trigger the message. On the <span class="strong"><strong>consumer</strong></span> side you can use the <code class="literal">some_label</code> to trigger the message.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_output_triggered_by_a_message" href="#_output_triggered_by_a_message"></a>Output triggered by a message</h4></div></div></div><p>The output message can be triggered by receiving a message.</p><pre class="programlisting">def dsl = Contract.make {
description <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Some Description'</span>
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// input is a message</span>
input {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the message was received from this destination</span>
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'input'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// has the following body</span>
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and the following headers</span>
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>In this case the output message will be sent to <code class="literal">output</code> if a proper message will be received on the <code class="literal">input</code> destination. In the message <span class="strong"><strong>publisher&#8217;s</strong></span> side
we will generate a test that will send the input message to the defined destination. On the <span class="strong"><strong>consumer</strong></span> side you can either send a message to the input
destination or use the <code class="literal">some_label</code> to trigger the message.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_consumer_producer" href="#_consumer_producer"></a>Consumer / Producer</h4></div></div></div><p>In HTTP you have a notion of <code class="literal">client</code>/<code class="literal">stub and `server</code>/<code class="literal">test</code> notation. You can use them also in messaging but we&#8217;re providing also the <code class="literal">consumer</code> and <code class="literal">produer</code> methods
as presented below (note you can use either <code class="literal">$</code> or <code class="literal">value</code> methods to provide <code class="literal">consumer</code> and <code class="literal">producer</code> parts)</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>))
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>))
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
}
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_multiple_contracts_in_one_file" href="#_multiple_contracts_in_one_file"></a>2.17.11&nbsp;Multiple contracts in one file</h3></div></div></div><p>It&#8217;s possible to define multiple contracts in one file. An example of such a contract can look like this</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
[
Contract.make {
name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"should post a user"</span>)
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/1'</span>)
}
response {
status <span class="hl-number">200</span>
}
},
Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/2'</span>)
}
response {
status <span class="hl-number">200</span>
}
}
]</pre><p>In this example one contract has the <code class="literal">name</code> field and the other doesn&#8217;t. This will lead to generation of
two tests that will look more or less like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.tests.com.hello;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.TestBase;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.jsonpath.DocumentContext;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.jsonpath.JsonPath;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.restassured.response.ResponseOptions;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Test;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.assertj.core.api.Assertions.assertThat;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> V1Test <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> TestBase {
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_should_post_a_user() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/1"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
}
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_withList_<span class="hl-number">1</span>() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/2"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
}
}</pre><p>Notice that for the contract that has the <code class="literal">name</code> field the generated test method is named
<code class="literal">validate_should_post_a_user</code>. For the one that doesn&#8217;t have the name it&#8217;s called
<code class="literal">validate_withList_1</code>. It corresponds to the name of the file <code class="literal">WithList.groovy</code> and the
index of the contract in the list.</p><p>The generated stubs will look like this</p><pre class="screen">should post a user.json
1_WithList.json</pre><p>As you can see the first file got the <code class="literal">name</code> parameter from the contract. The second
got the name of the contract file <code class="literal">WithList.groovy</code> prefixed with the index (in this case
contract had index <code class="literal">1</code> in the list of contracts in the file).</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>As you can see it&#8217;s much better if you name your contracts since then your tests
are far more meaningful.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customization" href="#_customization"></a>2.18&nbsp;Customization</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_extending_the_dsl" href="#_extending_the_dsl"></a>2.18.1&nbsp;Extending the DSL</h3></div></div></div><p>It is possible to provide your own functions to the DSL. The key requirement for this
feature was to maintain the static compatibility. Below you will be able to see an example
of:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">creation of a JAR with reusable classes</li><li class="listitem">referencing of these classes in the DSLs</li></ul></div><p>The full example can be found <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples" target="_top">here</a>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_common_jar" href="#_common_jar"></a>Common JAR</h4></div></div></div><p>Below you can find three classes that we will reuse in the DSLs.</p><p><span class="strong"><strong>PatternUtils</strong></span> contains functions used by both the <span class="strong"><strong>consumer</strong></span> and the <span class="strong"><strong>producer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.regex.Pattern;
<strong class="hl-tag" style="color: blue">/**
* If you want to use {@link Pattern} directly in your tests
* then you can create a class resembling this one. It can
* contain all the {@link Pattern} you want to use in the DSL.
*
* &lt;pre&gt;
* {@code
* request {
* body(
* [ age: $(c(PatternUtils.oldEnough()))]
* )
* }
* &lt;/pre&gt;
*
* Notice that we're using both {@code $()} for dynamic values
* and {@code c()} for the consumer side.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PatternUtils {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> String tooYoung() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-1][0-9]"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern oldEnough() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[2-9][0-9]"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
<strong class="hl-tag" style="color: blue">/**
* Makes little sense but it's just an example ;)
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern ok() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre><p><span class="strong"><strong>ConsumerUtils</strong></span> contains functions used by the <span class="strong"><strong>consumer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.ClientDslProperty;
<strong class="hl-tag" style="color: blue">/**
* DSL Properties passed to the DSL from the consumer's perspective.
* That means that on the input side {@code Request} for HTTP
* or {@code Input} for messaging you can have a regular expression.
* On the {@code Response} for HTTP or {@code Output} for messaging
* you have to have a concrete value.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ConsumerUtils {
<strong class="hl-tag" style="color: blue">/**
* Consumer side property. By using the {@link ClientDslProperty}
* you can omit most of boilerplate code from the perspective
* of dynamic values. Example
*
* &lt;pre&gt;
* {@code
* request {
* body(
* [ age: $(ConsumerUtils.oldEnough())]
* )
* }
* &lt;/pre&gt;
*
* That way it's in the implementation that we decide what value we will pass to the consumer
* and which one to the producer.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> ClientDslProperty oldEnough() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// this example is not the best one and</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// theoretically you could just pass the regex instead of `ServerDslProperty` but</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it's just to show some new tricks :)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ClientDslProperty(PatternUtils.oldEnough(), <span class="hl-number">40</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre><p><span class="strong"><strong>ProducerUtils</strong></span> contains functions used by the <span class="strong"><strong>producer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.ServerDslProperty;
<strong class="hl-tag" style="color: blue">/**
* DSL Properties passed to the DSL from the producer's perspective.
* That means that on the input side {@code Request} for HTTP
* or {@code Input} for messaging you have to have a concrete value.
* On the {@code Response} for HTTP or {@code Output} for messaging
* you can have a regular expression.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ProducerUtils {
<strong class="hl-tag" style="color: blue">/**
* Producer side property. By using the {@link ProducerUtils}
* you can omit most of boilerplate code from the perspective
* of dynamic values. Example
*
* &lt;pre&gt;
* {@code
* response {
* body(
* [ status: $(ProducerUtils.ok())]
* )
* }
* &lt;/pre&gt;
*
* That way it's in the implementation that we decide what value we will pass to the consumer
* and which one to the producer.
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> ServerDslProperty ok() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// this example is not the best one and</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// theoretically you could just pass the regex instead of `ServerDslProperty` but</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it's just to show some new tricks :)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ServerDslProperty( PatternUtils.ok(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>);
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_adding_the_dependency_to_project" href="#_adding_the_dependency_to_project"></a>Adding the dependency to project</h4></div></div></div><p>In order for the plugins and IDE to be able to reference the common JAR classes you need
to pass the dependency to your project.</p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_test_dependency_in_project_s_dependencies" href="#_test_dependency_in_project_s_dependencies"></a>Test dependency in project&#8217;s dependencies</h5></div></div></div><p>First add the common jar dependency as a test dependency. That way since your
contracts files are available at test resources path, automatically the
common jar classes will be visible in your Groovy files.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-common<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-common:0.0.1-SNAPSHOT"</span>)</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_test_dependency_in_plugin_s_dependencies" href="#_test_dependency_in_plugin_s_dependencies"></a>Test dependency in plugin&#8217;s dependencies</h5></div></div></div><p>Now you have to add the dependency for the plugin to reuse at runtime.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*intoxication.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.intoxication.BeerIntoxicationBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-common<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>compile<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-common:0.0.1-SNAPSHOT"</span></pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_referencing_classes_in_dsls" href="#_referencing_classes_in_dsls"></a>Referencing classes in DSLs</h5></div></div></div><p>Now you can reference your classes in your DSL. Example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts.beer.rest
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.ConsumerUtils
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.ProducerUtils
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
Contract.make {
description(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span>Represents a successful scenario of getting a beer
```
given:
client is old enough
when:
he applies <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> a beer
then:
we<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'ll grant him the beer
</span>```
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")
</span> request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/check'</span>
body(
age: $(ConsumerUtils.oldEnough())
)
headers {
contentType(applicationJson())
}
}
response {
status <span class="hl-number">200</span>
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${value(ProducerUtils.ok())}"</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")
</span> headers {
contentType(applicationJson())
}
}
}</pre></div></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_pluggable_architecture" href="#_pluggable_architecture"></a>2.19&nbsp;Pluggable architecture</h2></div></div></div><p>There are cases where you have your contracts defined in other formats
like YAML, RAML or PACT. On the other hand you&#8217;d like to profit from
the test and stubs generation. It&#8217;s really easy to add your own implementation
of either of those. Also you can customize the way tests are generated (for example you can generate
tests for other languages) and you can do the same for stubs generation (you can generate
stubs for other stub http server implementations).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_contract_converter" href="#_custom_contract_converter"></a>2.19.1&nbsp;Custom contract converter</h3></div></div></div><p>Let&#8217;s assume that your contract is written in a YAML file like this:</p><pre class="programlisting">request:
url: /foo
method: PUT
headers:
foo: bar
body:
foo: bar
response:
status: 200
headers:
foo2: bar
body:
foo2: bar</pre><p>Thanks to the interface</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.spec
<strong class="hl-tag" style="color: blue">/**
* Converter to be used to convert FROM {@link File} TO {@link Contract}
* and from {@link Contract} to {@code T}
*
* @param &lt;T&gt; - type to which we want to convert the contract
*
* @author Marcin Grzejszczak
* @since 1.1.0
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> ContractConverter&lt;T&gt; {
<strong class="hl-tag" style="color: blue">/**
* Should this file be accepted by the converter. Can use the file extension
* to check if the conversion is possible.
*
* @param file - file to be considered for conversion
* @return - {@code true} if the given implementation can convert the file
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isAccepted(File file)
<strong class="hl-tag" style="color: blue">/**
* Converts the given {@link File} to its {@link Contract} representation
*
* @param file - file to convert
* @return - {@link Contract} representation of the file
*/</strong>
Collection&lt;Contract&gt; convertFrom(File file)
<strong class="hl-tag" style="color: blue">/**
* Converts the given {@link Contract} to a {@link T} representation
*
* @param contract - the parsed contract
* @return - {@link T} the type to which we do the conversion
*/</strong>
T convertTo(Collection&lt;Contract&gt; contract)
}</pre><p>you can register your own implementation of a contract structure converter.
Your implementation needs to state the condition on which it should start the
conversion. Also you have to define how to perform that conversion in both ways.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Once you create your implementation you have to create a <code class="literal">/META-INF/spring.factories</code>
file in which you provide the fully qualified name of your implementation.</p></td></tr></table></div><p>Example of a <code class="literal">spring.factories</code> file</p><pre class="screen"># Converters
org.springframework.cloud.contract.spec.ContractConverter=\
org.springframework.cloud.contract.verifier.converter.YamlContractConverter</pre><p>and the YAML implementation</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.converter
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.nio.file.Files
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> groovy.transform.CompileStatic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.ContractConverter
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.Headers
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.yaml.snakeyaml.Yaml
<strong class="hl-tag" style="color: blue">/**
* Simple converter from and to a {@link YamlContract} to a collection of {@link Contract}
*/</strong>
<em><span class="hl-annotation" style="color: gray">@CompileStatic</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> YamlContractConverter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> ContractConverter&lt;List&lt;YamlContract&gt;&gt; {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isAccepted(File file) {
String name = file.getName()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> name.endsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".yml"</span>) || name.endsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".yaml"</span>)
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Collection&lt;Contract&gt; convertFrom(File file) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
YamlContract yamlContract = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Yaml().loadAs(
Files.newInputStream(file.toPath()), YamlContract.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> [Contract.make {
request {
method(yamlContract?.request?.method)
url(yamlContract?.request?.url)
headers {
yamlContract?.request?.headers?.each { String key, Object value -&gt;
header(key, value)
}
}
body(yamlContract?.request?.body)
}
response {
status(yamlContract?.response?.status)
headers {
yamlContract?.response?.headers?.each { String key, Object value -&gt;
header(key, value)
}
}
body(yamlContract?.response?.body)
}
}]
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (FileNotFoundException e) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> IllegalStateException(e)
}
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> List&lt;YamlContract&gt; convertTo(Collection&lt;Contract&gt; contracts) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> contracts.collect { Contract contract -&gt;
YamlContract yamlContract = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> YamlContract()
yamlContract.request.with {
method = contract?.request?.method?.clientValue
url = contract?.request?.url?.clientValue
headers = (contract?.request?.headers as Headers)?.asStubSideMap()
body = contract?.request?.body?.clientValue as Map
}
yamlContract.response.with {
status = contract?.response?.status?.clientValue as Integer
headers = (contract?.response?.headers as Headers)?.asStubSideMap()
body = contract?.response?.body?.clientValue as Map
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> yamlContract
}
}
}</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_pact_converter" href="#_pact_converter"></a>Pact converter</h4></div></div></div><p>Spring Cloud Contract comes with an out of the box support for <a class="link" href="https://docs.pact.io/" target="_top">Pact</a> representation of contracts.
In other words instead of using the Groovy DSL you can use Pact files. In this section
we will present how to add such a support for your project.</p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_pact_contract" href="#_pact_contract"></a>Pact contract</h5></div></div></div><p>We will be working on the following example of a Pact contract. We&#8217;ve placed this file under
the <code class="literal">src/test/resources/contracts</code> folder.</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"provider"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Provider"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"consumer"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Consumer"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"interactions"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"description"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"path"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"clientId"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1234567890"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanAmount"</span>: <span class="hl-number">99999</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchingRules"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.body.clientId"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"match"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{10}"</span>
}
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span class="hl-number">200</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json;charset=UTF-8"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudCheckStatus"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejectionReason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchingRules"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.body.fraudCheckStatus"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"match"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>
}
}
}
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"metadata"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pact-specification"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"version"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2.0.0"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pact-jvm"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"version"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2.4.18"</span>
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_pact_for_producers" href="#_pact_for_producers"></a>Pact for producers</h5></div></div></div><p>On the producer side you have add to your plugin configuration two additional dependencies.
One is the Spring Cloud Contract Pact support and the other represents the current
Pact version that you&#8217;re using.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-spec-pact<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>au.com.dius<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>pact-jvm-model<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.4.18<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'au.com.dius:pact-jvm-model:2.4.18'</span></pre><p class="secondary">
</p><p>When you execute the build of your application a test, looking more or less like this, will be generated</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"clientId\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json;charset=UTF-8"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejectionReason"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.fraudCheckStatus"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>);
}</pre><p>and the stub looking like this</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"uuid"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"996ae5ae-6834-4db6-8fac-358ca187ab62"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : [ {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.loanAmount == 99999)]"</span>
}, {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.clientId =~ /([0-9]{10})/)]"</span>
} ]
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json;charset=UTF-8"</span>
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_pact_for_consumers" href="#_pact_for_consumers"></a>Pact for consumers</h5></div></div></div><p>On the producer side you have add to your project dependencies two additional dependencies.
One is the Spring Cloud Contract Pact support and the other represents the current
Pact version that you&#8217;re using.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-spec-pact<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>au.com.dius<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>pact-jvm-model<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.4.18<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-spec-pact"</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'au.com.dius:pact-jvm-model:2.4.18'</span></pre><p class="secondary">
</p></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_test_generator" href="#_custom_test_generator"></a>2.19.2&nbsp;Custom test generator</h3></div></div></div><p>If you want to generate tests for different languages than Java or you&#8217;re
not happy with the way we&#8217;re building Java tests for you then you can register
your own implementation to do that.</p><p>Thanks to the interface</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.builder
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.verifier.file.ContractMetadata
<strong class="hl-tag" style="color: blue">/**
* Builds a single test.
*
* @since 1.1.0
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> SingleTestGenerator {
<strong class="hl-tag" style="color: blue">/**
* Creates contents of a single test class in which all test scenarios from
* the contract metadata should be placed.
*
* @param properties - properties passed to the plugin
* @param listOfFiles - list of parsed contracts with additional metadata
* @param className - the name of the generated test class
* @param classPackage - the name of the package in which the test class should be stored
* @param includedDirectoryRelativePath - relative path to the included directory
* @return contents of a single test class
*/</strong>
String buildClass(ContractVerifierConfigProperties properties, Collection&lt;ContractMetadata&gt; listOfFiles,
String className, String classPackage, String includedDirectoryRelativePath)
<strong class="hl-tag" style="color: blue">/**
* Extension that should be appended to the generated test class. E.g. {@code .java} or {@code .php}
*
* @param properties - properties passed to the plugin
*/</strong>
String fileExtension(ContractVerifierConfigProperties properties)
}</pre><p>you can register your own implementation that generates a test. Again, it&#8217;s enough to provide
a proper <code class="literal">spring.factories</code> file. Example:</p><pre class="screen">org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
com.example.MyGenerator</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_stub_generator" href="#_custom_stub_generator"></a>2.19.3&nbsp;Custom stub generator</h3></div></div></div><p>If you want to generate stubs for other stub server than WireMock it&#8217;s enough to
plug in your own implementation of this interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.converter
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> groovy.transform.CompileStatic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.verifier.file.ContractMetadata
<strong class="hl-tag" style="color: blue">/**
* Converts contracts into their stub representation.
*
* @since 1.1.0
*/</strong>
<em><span class="hl-annotation" style="color: gray">@CompileStatic</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StubGenerator {
<strong class="hl-tag" style="color: blue">/**
* Returns {@code true} if the converter can handle the file to convert it into a stub.
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> canHandleFileName(String fileName)
<strong class="hl-tag" style="color: blue">/**
* Returns the collection of converted contracts into stubs. One contract can
* result in multiple stubs.
*/</strong>
Map&lt;Contract, String&gt; convertContents(String rootName, ContractMetadata content)
<strong class="hl-tag" style="color: blue">/**
* Returns the name of the converted stub file. If you have multiple contracts
* in a single file then a prefix will be added to the generated file. If you
* provide the {@link Contract#name} field then that field will override the
* generated file name.
*
* Example: name of file with 2 contracts is {@code foo.groovy}, it will be
* converted by the implementation to {@code foo.json}. The recursive file
* converter will create two files {@code 0_foo.json} and {@code 1_foo.json}
*/</strong>
String generateOutputFileNameForInput(String inputFileName)
}</pre><p>you can register your own implementation that generate Stubs. Again, it&#8217;s enough to provide
a proper <code class="literal">spring.factories</code> file. Example:</p><pre class="screen"># Stub converters
org.springframework.cloud.contract.verifier.converter.StubGenerator=\
org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter</pre><p>The default implementation is the WireMock stub generation.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You can provide multiple stub generator implementations. That way for example from a single
DSL as input you can e.g. produce WireMock stubs and Pact files too!</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_stub_runner" href="#_custom_stub_runner"></a>2.19.4&nbsp;Custom Stub Runner</h3></div></div></div><p>If you decide to have a custom stub generation you also need a custom way of running
stubs with your different stub provider.</p><p>Let us assume that you&#8217;re using <a class="link" href="https://github.com/dreamhead/moco" target="_top">Moco</a> to build your stubs.
You wrote a proper stub generator and your stubs got placed in a JAR file.</p><p>In order for Stub Runner to know how to run your stubs you have to define a custom
HTTP Stub server implementation. It can look like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.stubrunner.provider.moco
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.dreamhead.moco.bootstrap.arg.HttpArgs
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.dreamhead.moco.runner.JsonRunner
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.dreamhead.moco.runner.RunnerSetting
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> groovy.util.logging.Slf4j
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.stubrunner.HttpServerStub
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.util.SocketUtils
<em><span class="hl-annotation" style="color: gray">@Slf4j</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MocoHttpServerStub <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> HttpServerStub {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> started
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> JsonRunner runner
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (!isRunning()) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> -<span class="hl-number">1</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> port
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isRunning() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> started
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub start() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> start(SocketUtils.findAvailableTcpPort())
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub start(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port = port
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub stop() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (!isRunning()) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.runner.stop()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub registerMappings(Collection&lt;File&gt; stubFiles) {
List&lt;RunnerSetting&gt; settings = stubFiles.findAll { it.name.endsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"json"</span>) }
.collect {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Trying to parse [{}]"</span>, it.name)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> RunnerSetting.aRunnerSetting().withStream(it.newInputStream()).build()
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (Exception e) {
log.warn(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Exception occurred while trying to parse file [{}]"</span>, it.name, e)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null
}
}.findAll { it }
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.runner = JsonRunner.newJsonRunnerWithSetting(settings,
HttpArgs.httpArgs().withPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port).build())
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.runner.run()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.started = true
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
String registeredMappings() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isAccepted(File file) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> file.name.endsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".json"</span>)
}
}</pre><p>and just register it in your <code class="literal">spring.factories</code> file</p><pre class="screen">org.springframework.cloud.contract.stubrunner.HttpServerStub=\
org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub</pre><p>that way you&#8217;ll be able to run stubs using Moco.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you don&#8217;t provide any implementation then the default one - WireMock based
will be picked. If you provide more than one then the first one on the list will be picked.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_stub_downloader" href="#_custom_stub_downloader"></a>2.19.5&nbsp;Custom Stub Downloader</h3></div></div></div><p>You can customize the way your stubs are downloaded. It&#8217;s enough to create an
implementation of the <code class="literal">StubDownloaderBuilder</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomStubDownloaderBuilder <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> StubDownloaderBuilder {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> StubDownloader build(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> StubRunnerOptions stubRunnerOptions) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubDownloader() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Map.Entry&lt;StubConfiguration, File&gt; downloadAndUnpackStubJar(
StubConfiguration config) {
File unpackedStubs = retrieveStubs();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AbstractMap.SimpleEntry&lt;&gt;(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
config.getClassifier()), unpackedStubs);
}
File retrieveStubs() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// here goes your custom logic to provide a folder where all the stubs reside</span>
}
}</pre><p>and just register it in your <code class="literal">spring.factories</code> file</p><pre class="screen"># Example of a custom Stub Downloader Provider
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
com.example.CustomStubDownloaderBuilder</pre><p>that way you&#8217;ll be able to pick a folder with the source of your stubs.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you don&#8217;t provide any implementation then the default one will be picked.
If you provide <code class="literal">repositoryRoot</code> property or <code class="literal">workOffline</code> flag then Aether based
that will download stubs from a remote repo will be picked. If you don&#8217;t provide these
values then the <code class="literal">ClasspathStubProvider</code> will be picked that will scan the classpath.
If you provide more than one, then the first one on the list will be picked.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_links" href="#_links"></a>2.20&nbsp;Links</h2></div></div></div><p>Here you can find interesting links related to Spring Cloud Contract Verifier:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/" target="_top">Spring Cloud Contract Github Repository</a></li><li class="listitem"><a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/" target="_top">Spring Cloud Contract Samples</a></li><li class="listitem"><a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html" target="_top">Spring Cloud Contract Documentation</a></li><li class="listitem"><a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/deprecated" target="_top">Accurest Legacy Documentation</a></li><li class="listitem"><a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#spring-cloud-contract-stub-runner" target="_top">Spring Cloud Contract Stub Runner Documentation</a></li><li class="listitem"><a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#stub-runner-for-messaging" target="_top">Spring Cloud Contract Stub Runner Messaging Documentation</a></li><li class="listitem"><a class="link" href="https://gitter.im/spring-cloud/spring-cloud-contract" target="_top">Spring Cloud Contract Gitter</a></li><li class="listitem"><a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/" target="_top">Spring Cloud Contract Maven Plugin</a></li><li class="listitem"><a class="link" href="https://www.youtube.com/watch?v=sAAklvxmPmk" target="_top">Spring Cloud Contract WJUG Presentation by Marcin Grzejszczak</a></li></ul></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__spring_cloud_contract.html">Prev</a>&nbsp;</td><td width="20%" align="center">&nbsp;</td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_contract_wiremock.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">1.&nbsp;Spring Cloud Contract&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud-contract.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;3.&nbsp;Spring Cloud Contract WireMock</td></tr></table></div></body></html>