Sync docs from master to gh-pages

This commit is contained in:
Dave Syer
2016-07-27 10:06:55 +01:00
parent 8fcca5840a
commit a88d647e77
12 changed files with 630 additions and 629 deletions

View File

@@ -440,7 +440,6 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
<ul class="sectlevel4">
<li><a href="#_consumer_side_loan_issuance">Consumer side (Loan Issuance)</a></li>
<li><a href="#_producer_side_fraud_detection_server">Producer side (Fraud Detection server)</a></li>
<li><a href="#_consumer_side_loan_issuance_final_step">Consumer side (Loan Issuance) final step</a></li>
</ul>
</li>
<li><a href="#_dependencies">Dependencies</a></li>
@@ -655,8 +654,14 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
<li><a href="#_links">Links</a></li>
</ul>
</li>
<li><a href="#_spring_cloud_contract_wiremock">Spring Cloud Contract WireMock</a></li>
<li><a href="#_spring_cloud_contract_wiremock">Spring Cloud Contract WireMock</a>
<ul class="sectlevel2">
<li><a href="#_registering_stubs_automatically">Registering Stubs Automatically</a></li>
<li><a href="#_alternative_using_junit_rules">Alternative: Using JUnit Rules</a></li>
</ul>
</li>
<li><a href="#_wiremock_and_spring_mvc_mocks">WireMock and Spring MVC Mocks</a></li>
<li><a href="#_generating_stubs_using_restdocs">Generating Stubs using RestDocs</a></li>
</ul>
</div>
</div>
@@ -928,18 +933,7 @@ going through the process. CDC is all about communication.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">@Test
public void shouldBeRejectedDueToAbnormalLoanAmount() {
// given:
LoanApplication application = new LoanApplication(new Client("1234567890"),
99999);
// when:
LoanApplicationResult loanApplication = sut.loanApplication(application);
// then:
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high");
}</code></pre>
<pre class="highlight"><code class="language-groovy" data-lang="groovy">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=client_tdd,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
@@ -953,10 +947,7 @@ public void shouldBeRejectedDueToAbnormalLoanAmount() {
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">ResponseEntity&lt;FraudServiceResponse&gt; response =
restTemplate.exchange("http://localhost:" + port + "/fraudcheck", HttpMethod.PUT,
new HttpEntity&lt;&gt;(request, httpHeaders),
FraudServiceResponse.class);</code></pre>
<pre class="highlight"><code class="language-groovy" data-lang="groovy">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/src/main/java/com/example/loan/LoanApplicationService.java[tags=client_call_server,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
@@ -984,276 +975,142 @@ public void shouldBeRejectedDueToAbnormalLoanAmount() {
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">package contracts
<pre class="highlight"><code class="language-groovy" data-lang="groovy">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/test/resources/contracts/shouldMarkClientAsFraud.groovy[]
org.springframework.cloud.contract.spec.Contract.make {
request { // (1)
method 'PUT' // (2)
url '/fraudcheck' // (3)
body([ // (4)
clientId: value(consumer(regex('[0-9]{10}'))),
loanAmount: 99999
])
headers { // (5)
header('Content-Type', 'application/vnd.fraud.v1+json')
}
}
response { // (6)
status 200 // (7)
body([ // (8)
fraudCheckStatus: "FRAUD",
rejectionReason: "Amount too high"
])
headers { // (9)
header('Content-Type': value(
producer(regex('application/vnd.fraud.v1.json.*')),
consumer('application/vnd.fraud.v1+json'))
)
}
}
}
/*
Since we don't want to force on the user to hardcode values of fields that are dynamic
(timestamps, database ids etc.), one can provide parametrize those entries by using the
`value(consumer(...), producer(...))` method. That way what's present in the `consumer`
section will end up in the produced stub. What's there in the `producer` will end up in the
autogenerated test. If you provide only the regular expression side without the concrete
value then Spring Cloud Contract will generate one for you.
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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+json.*`
*/</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Contract is written using a statically typed Groovy DSL. You might be wondering what are those
<code>value(client(&#8230;&#8203;), server(&#8230;&#8203;))</code> parts. By using this notation Spring Cloud Contract allows you to
The Contract is written using a statically typed Groovy DSL. You might be wondering what are those
`value(client(...), server(...))` 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
don't want to hardcode a value. You want to allow some different ranges of values. That'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 href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_contract_dsl">Consult the docs
for more information.</a> We highly recommend using the map notation!</p>
https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_contract_dsl[Consult the docs
for more information.] We highly recommend using the map notation!
The aforementioned contract is an agreement between two sides that:
- if an HTTP request is sent with
** a method `PUT` on an endpoint `/fraudcheck`
** JSON body with `clientId` matching the regular expression `[0-9]{10}` and `loanAmount` equal to `99999`
** and with a header `Content-Type` equal to `application/vnd.fraud.v1+json`
- then an HTTP response would be sent to the consumer that
** has status `200`
** contains JSON body with the `fraudCheckStatus` field containing a value `FRAUD` and the `rejectionReason` field having value `Amount too high`
** and a `Content-Type` header with a value of `application/vnd.fraud.v1+json`
Once we're ready to check the API in practice in the integration tests we need to just install the stubs locally
*add the Spring Cloud Contract Verifier plugin*
We can add either Maven or Gradle plugin - in this example we'll show how to add Maven. First we need to add the `Spring Cloud Contract` BOM.
[source,xml,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
<p>The aforementioned contract is an agreement between two sides that:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>if an HTTP request is sent with</p>
<div class="ulist">
<ul>
<li>
<p>a method <code>PUT</code> on an endpoint <code>/fraudcheck</code></p>
</li>
<li>
<p>JSON body with <code>clientId</code> matching the regular expression <code>[0-9]{10}</code> and <code>loanAmount</code> equal to <code>99999</code></p>
</li>
<li>
<p>and with a header <code>Content-Type</code> equal to <code>application/vnd.fraud.v1+json</code></p>
</li>
</ul>
</div>
</li>
<li>
<p>then an HTTP response would be sent to the consumer that</p>
<div class="ulist">
<ul>
<li>
<p>has status <code>200</code></p>
</li>
<li>
<p>contains JSON body with the <code>fraudCheckStatus</code> field containing a value <code>FRAUD</code> and the <code>rejectionReason</code> field having value <code>Amount too high</code></p>
</li>
<li>
<p>and a <code>Content-Type</code> header with a value of <code>application/vnd.fraud.v1+json</code></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<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>
</div>
<div class="paragraph">
<p><strong>add the Spring Cloud Contract Verifier plugin</strong></p>
</div>
<div class="paragraph">
<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>Spring Cloud Contract</code> BOM.</p>
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/pom.xml[tags=contract_bom,indent=0]</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-dependencies&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/dependencyManagement&gt;</code></pre>
<pre>Next, the `Spring Cloud Contract Verifier` Maven plugin
[source,xml,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>Next, the <code>Spring Cloud Contract Verifier</code> Maven plugin</p>
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/pom.xml[tags=contract_maven_plugin,indent=0]</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&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;baseClassForTests&gt;com.example.fraud.MvcTest&lt;/baseClassForTests&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;</code></pre>
<pre>Since the plugin was added we get the `Spring Cloud Contract Verifier` features which from the provided contracts:
- generate and run tests
- produce and install stubs
We don't want to generate tests since we, as consumers, want only to play with the stubs. That's why we need to skip the tests generation and execution. When we execute:
[source,bash,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>Since the plugin was added we get the <code>Spring Cloud Contract Verifier</code> features which from the provided contracts:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>generate and run tests</p>
</li>
<li>
<p>produce and install stubs</p>
</li>
</ul>
</div>
<div class="paragraph">
<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>
<p>cd local-http-server-repo
./mvnw clean install -DskipTests</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">cd local-http-server-repo
./mvnw clean install -DskipTests</code></pre>
<pre>In the logs we'll see something like this:
[source,bash,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>In the logs we&#8217;ll see something like this:</p>
<p>[INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">[INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
[INFO]
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:1.4.0.BUILD-SNAPSHOT:repackage (default) @ http-server ---
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
<div class="paragraph">
<p>[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar</p>
</div>
<div class="paragraph">
<p>[INFO] --- spring-boot-maven-plugin:1.4.0.BUILD-SNAPSHOT:repackage (default) @ http-server ---</p>
</div>
<div class="paragraph">
<p>[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.jar
[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.pom
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar</code></pre>
</div>
</div>
<div class="paragraph">
<p>This line is extremely important</p>
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar</code></pre>
<pre>This line is extremely important
[source,bash,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>It&#8217;s confirming that the stubs of the <code>http-server</code> have been installed in the local repository.</p>
</div>
<div class="paragraph">
<p><strong>run the integration tests</strong></p>
</div>
<div class="paragraph">
<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>Loan Application service</code>).</p>
</div>
<div class="paragraph">
<p>Add the <code>Spring Cloud Contract</code> BOM</p>
<p>[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-dependencies&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/dependencyManagement&gt;</code></pre>
<pre>It's confirming that the stubs of the `http-server` have been installed in the local repository.
*run the integration tests*
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 (`Loan Application service`).
Add the `Spring Cloud Contract` BOM
[source,xml,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>Add the dependency to <code>Spring Cloud Contract Stub Runner</code></p>
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/pom.xml[tags=contract_bom,indent=0]</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-wiremock&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-contract-stub-runner&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre>
<pre>Add the dependency to `Spring Cloud Contract Stub Runner`
[source,xml,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>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>
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/pom.xml[tags=stub_runner,indent=0]</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-yaml" data-lang="yaml">stubrunner:
work-offline: true
stubs.ids: 'com.example:http-server:+:stubs:8080'</code></pre>
<pre>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're playing with the collaborators offline (optional step).
[source,yaml,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/src/test/resources/application.yaml[]</p>
</div>
<div class="paragraph">
<p>Annotate your test class with <code>@AutoConfigureStubRunner</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureStubRunner
public class LoanApplicationServiceTests {</code></pre>
<pre class="highlight"><code class="language-groovy" data-lang="groovy">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=autoconfigure_stubrunner,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1296,13 +1153,8 @@ public class LoanApplicationServiceTests {</code></pre>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@RequestMapping(
value = "/fraudcheck",
method = PUT,
consumes = FRAUD_SERVICE_JSON_VERSION_1,
produces = FRAUD_SERVICE_JSON_VERSION_1)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
<pre class="highlight"><code class="language-java" data-lang="java">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=server_api,indent=0]
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
}</code></pre>
</div>
</div>
@@ -1320,11 +1172,7 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr</co
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-contract-verifier&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre>
<pre class="highlight"><code class="language-xml" data-lang="xml">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/pom.xml[tags=verifier_test_dependencies,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1332,15 +1180,7 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr</co
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&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;baseClassForTests&gt;com.example.fraud.MvcTest&lt;/baseClassForTests&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;</code></pre>
<pre class="highlight"><code class="language-xml" data-lang="xml">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/pom.xml[tags=contract_maven_plugin,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
@@ -1348,138 +1188,119 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr</co
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">package com.example.fraud;
<pre class="highlight"><code class="language-java" data-lang="java">Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/test/java/com/example/fraud/MvcTest.java[]
import com.example.fraud.FraudDetectionController;
import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
Now, if you run the `./mvnw clean install` you would get sth like this:
import org.junit.Before;
public class MvcTest {
@Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new FraudDetectionController());
}
public void assertThatRejectionReasonIsNull(Object rejectionReason) {
assert rejectionReason == null;
}
}</code></pre>
[source,bash,indent=0]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now, if you run the <code>./mvnw clean install</code> you would get sth like this:</p>
<p>Results :</p>
</div>
<div class="paragraph">
<p>Tests in error:
ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed&#8230;&#8203;</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">Results :
<pre>That's because you have a new contract from which a test was generated and it failed since you haven't implemented the feature. The autogenerated test would look like this:
Tests in error:
ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed...</code></pre>
[source,java,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<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>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@Test
<p>@Test
public void validate_shouldMarkClientAsFraud() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/vnd.fraud.v1+json")
.body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
// when:
ResponseOptions response = given().spec(request)
.put("/fraudcheck");
// then:
.body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");</p>
</div>
<div class="literalblock">
<div class="content">
<pre>// when:
ResponseOptions response = given().spec(request)
.put("/fraudcheck");</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre> // then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("fraudCheckStatus").matches("[A-Z]{5}");
assertThatJson(parsedJson).field("rejectionReason").isEqualTo("Amount too high");
}</code></pre>
}</pre>
</div>
</div>
<div class="paragraph">
<p>As you can see all the <code>producer()</code> parts of the Contract that were present in the <code>value(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code> blocks got injected into the test.</p>
</div>
<div class="paragraph">
<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>red</code> part of <code>red</code>, <code>green</code> and <code>refactor</code>. Time to convert the <code>red</code> into the <code>green</code>.</p>
</div>
<div class="paragraph">
<p><strong>write the missing implementation</strong></p>
</div>
<div class="paragraph">
<p>Now since we now what is the expected input and expected output let&#8217;s write the missing implementation.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@RequestMapping(
value = "/fraudcheck",
method = PUT,
consumes = FRAUD_SERVICE_JSON_VERSION_1,
produces = FRAUD_SERVICE_JSON_VERSION_1)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
if (amountGreaterThanThreshold(fraudCheck)) {
return new FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
}
return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}</code></pre>
<pre>As you can see all the `producer()` parts of the Contract that were present in the `value(consumer(...), producer(...))` blocks got injected into the test.
What'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 `red` part of `red`, `green` and `refactor`. Time to convert the `red` into the `green`.
*write the missing implementation*
Now since we now what is the expected input and expected output let's write the missing implementation.
[source,java,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>If we execute <code>./mvnw clean install</code> again the tests will pass. Since the <code>Spring Cloud Contract Verifier</code> plugin adds the tests to the <code>generated-test-sources</code> you can actually run those tests from your IDE.</p>
</div>
<div class="paragraph">
<p><strong>deploy your app</strong></p>
</div>
<div class="paragraph">
<p>Once you&#8217;ve finished your work it&#8217;s time to deploy your change. First merge the branch</p>
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=server_api,indent=0]
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=new_impl,indent=0]
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
}</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">git checkout master
<pre>If we execute `./mvnw clean install` again the tests will pass. Since the `Spring Cloud Contract Verifier` plugin adds the tests to the `generated-test-sources` you can actually run those tests from your IDE.
*deploy your app*
Once you've finished your work it's time to deploy your change. First merge the branch
[source,bash,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>git checkout master
git merge --no-ff contract-change-pr
git push origin master</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then we assume that your CI would run sth like <code>./mvnw clean deploy</code> which would publish both the application and the stub artifcats.</p>
</div>
</div>
<div class="sect4">
<h5 id="_consumer_side_loan_issuance_final_step">Consumer side (Loan Issuance) final step</h5>
<div class="paragraph">
<p>As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):</p>
</div>
<div class="paragraph">
<p><strong>merge branch to master</strong></p>
git push origin master</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">git checkout master
git merge --no-ff contract-change-pr</code></pre>
<pre>Then we assume that your CI would run sth like `./mvnw clean deploy` which would publish both the application and the stub artifcats.
===== Consumer side (Loan Issuance) final step
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):
*merge branch to master*
[source,bash,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p><strong>work online</strong></p>
</div>
<div class="paragraph">
<p>Now you can disable the offline work for Spring Cloud Contract Stub Runner ad 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.</p>
<p>git checkout master
git merge --no-ff contract-change-pr</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-yaml" data-lang="yaml">stubrunner.stubs:
ids: 'com.example:http-server:+:stubs:8080'
repositoryRoot: http://repo.spring.io/libs-snapshot</code></pre>
<pre>*work online*
Now you can disable the offline work for Spring Cloud Contract Stub Runner ad 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.
[source,yaml,indent=0]</pre>
</div>
</div>
<div class="paragraph">
<p>Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/http-client/src/test/resources/application-test-repo.yaml[]</p>
</div>
<div class="paragraph">
<p>And that&#8217;s it!</p>
</div>
</div>
@@ -5062,6 +4883,35 @@ public class WiremockForDocsTests {
<div class="paragraph">
<p>To start the stub server on a different port use <code>@AutoConfigureWireMock(port=9999)</code> (for example), and for a random port use the value 0. The stub server port will be bindable in the test application context as "wiremock.server.port". Using <code>@AutoConfigureWireMock</code> adds a bean of type <code>WiremockConfiguration</code> to your test application context, where it will be cached in between methods and classes having the same context, just like for normal Spring integration tests.</p>
</div>
<div class="sect2">
<h3 id="_registering_stubs_automatically">Registering Stubs Automatically</h3>
<div class="paragraph">
<p>If you add a <code>stubs</code> attribute to your <code>@AutoConfigureWireMock</code> then
it will register WireMock JSON stubs from the file system or
classpath. The stubs attribute can be a resource pattern (ant-style)
or a directory, in which case <code><strong>*/</strong>.json</code> is appended. Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_alternative_using_junit_rules">Alternative: Using JUnit Rules</h3>
<div class="paragraph">
<p>For a more conventional WireMock experience, using JUnit <code>@Rules</code> to
start and stop the server, just use the <code>WireMockSpring</code> convenience
@@ -5100,6 +4950,7 @@ public class WiremockForDocsClassRuleTests {
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_wiremock_and_spring_mvc_mocks">WireMock and Spring MVC Mocks</h2>
<div class="sectionbody">
@@ -5110,7 +4961,7 @@ Spring <code>MockRestServiceServer</code>. Here&#8217;s an example:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class WiremockForDocsMockServerApplicationTests {
@Autowired
@@ -5121,10 +4972,9 @@ public class WiremockForDocsMockServerApplicationTests {
@Test
public void contextLoads() throws Exception {
// will read stubs from default /resources/stubs location
MockRestServiceServer server = WireMockExpectations.with(this.restTemplate)
.baseUrl("http://example.org")
.expect("resource");
// will read stubs classpath
MockRestServiceServer server = WireMockRestServiceServer.with(this.restTemplate)
.baseUrl("http://example.org").stubs("classpath:/stubs/resource.json");
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World");
server.verify();
@@ -5133,14 +4983,16 @@ public class WiremockForDocsMockServerApplicationTests {
</div>
</div>
<div class="paragraph">
<p>The <code>baseUrl</code> is prepended to all mock calls, and the <code>expect()</code>
method takes a stub name as an argument, where the stubs are stored in
the classpath at <code>/stubs/&lt;name&gt;.json</code> by default. So in this example
the stub defined at <code>/stubs/resource.json</code> is loaded into the mock
server, so if the <code>RestTemplate</code> is asked to visit
<code><a href="http://example.org/" class="bare">http://example.org/</a></code> it will get the responses as declared there. The
JSON format is the normal WireMock format which you can read about in
the WireMock website.</p>
<p>The <code>baseUrl</code> is prepended to all mock calls, and the <code>stubs()</code>
method takes a stub path resource pattern as an argument. So in this
example the stub defined at <code>/stubs/resource.json</code> is loaded into the
mock server, so if the <code>RestTemplate</code> is asked to visit
<code><a href="http://example.org/" class="bare">http://example.org/</a></code> it will get the responses as declared
there. More than one stub pattern can be specified, and each one can
be a directory (for a recursive list of all ".json"), or a fixed
filename (like in the example above) or an ant-style pattern. The JSON
format is the normal WireMock format which you can read about in the
WireMock website.</p>
</div>
<div class="paragraph">
<p>Currently we support Tomcat, Jetty and Undertow as Spring Boot
@@ -5151,10 +5003,159 @@ Spring Boot container if there is one.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_generating_stubs_using_restdocs">Generating Stubs using RestDocs</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://projects.spring.io/spring-restdocs">Spring RestDocs</a> can be
used to generate documentation (e.g. in asciidoctor format) for an
HTTP API with Spring MockMvc or RestEasy. At the same time as you
generate documentation for your API, you can also generate WireMock
stubs, by using Spring Cloud Contract WireMock. Just write your normal
RestDocs test cases and use <code>@AutoConfigureRestDocs</code> to have stubs
automatically in the restdocs output directory. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(get("/resource"))
.andExpect(content().string("Hello World"))
.andDo(document("resource"));
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>From this test will be generated a WireMock stub at
"target/snippets/stubs/resource.json". It matches all GET requests to
the "/resource" path.</p>
</div>
<div class="paragraph">
<p>Without any additional configuration this will create a stub with a
request matcher for the HTTP method and all headers except "host" and
"content-length". To match the request more precisely, for example to
match the body of a POST or PUT, we need to explicitly create a
request matcher. This will do two things: 1) create a stub that only
matches the way you specify, 2) assert that the request in the test
case also matches the same conditions.</p>
</div>
<div class="paragraph">
<p>The main entry point for this is <code>WireMockRestDocs.verify()</code> which can
be used as a substitute for the <code>document()</code> convenience method. For
example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status.isOk())
.andDo(verify().jsonPath("$.id")
.stub("resource"));
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>So this contract is saying: any valid POST with an "id" field will get
back an the same response as in this test. You can chain together
calls to <code>.jsonPath()</code> to add additional matchers. The
<a href="https://github.com/jayway/JsonPath">JayWay documentation</a> can help you
to get up to speed with JSON Path if it is unfamiliar to you.</p>
</div>
<div class="paragraph">
<p>Instead of the <code>jsonPath</code> and <code>contentType</code> convenience methods, you
can also use the WireMock APIs to verify the request matches the
created stub. Example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status.isOk())
.andDo(verify()
.wiremock(WireMock.post(
urlPathEquals("/resource"))
.withRequestBody(matchingJsonPath("$.id"))
.stub("post-resource"));
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The WireMock API is rich - you can match headers, query parameters,
and request body by regex as well as by json path - so this can useful
to create stubs with a wider range of parameters. The above example
will generate a stub something like this:</p>
</div>
<div class="listingblock">
<div class="title">post-resource.json</div>
<div class="content">
<pre class="highlight"><code class="language-json" data-lang="json">{
"request" : {
"url" : "/resource",
"method" : "PUT",
"bodyPatterns" : [ {
"matchesJsonPath" : "$.id"
}]
},
"response" : {
"status" : 200,
"body" : "Hello World",
"headers" : {
"X-Application-Context" : "application:-1",
"Content-Type" : "text/plain"
}
}
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
You can use either the <code>wiremock()</code> method or the <code>jsonPath()</code>
and <code>contentType()</code> methods to create request matchers, but not both.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>On the consumer side, assuming the <code>resource.json</code> generated above is
available on the classpath, you can create a stub using WireMock in a
number of different ways, including as described above using
<code>@AutoConfigureWireMock(stubs="classpath:resource.json")</code>.</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-07-26 11:40:57 CEST
Last updated 2016-07-21 17:17:43 BST
</div>
</div>
</body>