Sync docs from master to gh-pages
This commit is contained in:
@@ -24,7 +24,197 @@ sides.</li><li class="listitem">To generate boilerplate test code to be used on
|
||||
features in the contracts. 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 case and one for the negative case. Contract tests are
|
||||
used to test contracts between applications and not to simulate full behavior.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_it_works" href="#_how_it_works"></a>2.3 How It Works</h2></div></div></div><p>This section explores how Spring Cloud Contract Verifier with Stub Runner works.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_defining_the_contract" href="#_defining_the_contract"></a>2.3.1 Defining the contract</h3></div></div></div><p>As consumers of services, we need to define what exactly we want to achieve. We need to
|
||||
used to test contracts between applications and not to simulate full behavior.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_it_works" href="#_how_it_works"></a>2.3 How It Works</h2></div></div></div><p>This section explores how Spring Cloud Contract Verifier with Stub Runner works.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_a_three_second_tour" href="#_a_three_second_tour"></a>2.3.1 A three second tour</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_on_the_producer_side" href="#_on_the_producer_side"></a>On the Producer Side</h4></div></div></div><p>In order to start working with <code class="literal">Spring Cloud Contract</code>, add files with REST/ messaging contracts expressed in either
|
||||
Groovy DSL or YAML to the contracts directory set by the
|
||||
<code class="literal">contractsDslDir</code> property, by default <code class="literal">$rootDir/src/test/resources/contracts</code>.</p><p>Then, add Spring Cloud Contract Verifier dependency and plugin to your build file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><plugin></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><version></span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></version></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><extensions></span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></extensions></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></plugin></span></pre><p>Now, running <code class="literal">./mvnw clean install</code> will cause tests that verify the application
|
||||
compliance with the added contracts to be automatically generated, by default under <code class="literal">org.springframework.cloud.contract.verifier.tests.</code>.</p><p>As the implementation of the functionalities described by the contracts is not yet present,
|
||||
the tests will fail.</p><p>To make them pass, the correct implementation of either handling HTTP requests or messages
|
||||
will have to be added. Also, a correct base test class for auto-generated tests needs to be added to the project.
|
||||
This class will be extended by all the auto-generated tests and it should contain all the setup
|
||||
necessary to run them (for example <code class="literal">RestAssuredMockMvc</code> controller setup or messaging test setup).</p><p>Once the implementation and the test base class are in place, the tests will pass, and both the application
|
||||
and the stub artifacts will be built and installed in the local Maven repository. The changes can now be merged
|
||||
and both the application and the stub artifacts may be published in an online repository.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_on_the_consumer_side" href="#_on_the_consumer_side"></a>On the Consumer Side</h4></div></div></div><p><code class="literal">Spring Cloud Contract Stub Runner</code> can be used in the integration tests to get a running WireMock instance/
|
||||
messaging route that simulates the actual service.</p><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"><dependency></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>Get the Producer-side stubs installed in your Maven repository by either:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">checking out the Producer side repository, adding contracts and generating the stubs by running:</li></ul></div><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><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 tests are being skipped because the Producer-side contract implementation is not in place yet,
|
||||
so the automatically-generated contract tests would fail;</p></td></tr></table></div><p>or:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">getting already existing producer service stubs from a remote repository; to do this, simply pass the
|
||||
stub artifact ids and artifact repository url as <code class="literal">Spring Cloud Contract Stub Runner</code> properties:</li></ul></div><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>: http://repo.spring.io/libs-snapshot</pre><p>Now just annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation, provide
|
||||
the group-id and artifact-id for <code class="literal">Spring Cloud Contract Stub Runner</code> to run the collaborators' stubs for you.</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>Use the <code class="literal">REMOTE</code> stubsMode when downloading stubs from an online repository and <code class="literal">LOCAL</code> for offline work.</p></td></tr></table></div><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"},
|
||||
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</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 in your integration test, you will be able to receive stubbed versions of HTTP responses or messages that are
|
||||
expected to be emitted by the collaborator service.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_a_three_minute_tour" href="#_a_three_minute_tour"></a>2.3.2 A three minute tour</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_on_the_producer_side_2" href="#_on_the_producer_side_2"></a>On the Producer Side</h4></div></div></div><p>In order to start working with <code class="literal">Spring Cloud Contract</code>, add files with REST/ messaging contracts expressed in either
|
||||
Groovy DSL or YAML to the contracts directory set by the
|
||||
<code class="literal">contractsDslDir</code> property, by default <code class="literal">$rootDir/src/test/resources/contracts</code>.</p><p>For the HTTP stubs, a contract defines what kind of response should be returned for a given request (taking into account the HTTP
|
||||
methods, urls, headers, status codes, etc.). A sample HTTP stub contract in Groovy DSL would look like this:</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 {
|
||||
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">'/fraudcheck'</span>
|
||||
body([
|
||||
<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 {
|
||||
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
|
||||
}
|
||||
}
|
||||
response {
|
||||
status <span class="hl-number">200</span>
|
||||
body([
|
||||
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 {
|
||||
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
|
||||
}
|
||||
}
|
||||
}</pre><p>While the same contract expressed in YAML would look the following way:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">request</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> method</span>: PUT
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: /fraudcheck
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> body</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> "client.id"</span>: <span class="hl-number">1234567890</span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> loanAmount</span>: <span class="hl-number">99999</span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> headers</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> Content-Type</span>: application/json
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> matchers</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> body</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - path</span>: $.[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'client.id'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> type</span>: by_regex
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> value</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-attribute">response</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> status</span>: <span class="hl-number">200</span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> body</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> 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-attribute"> "rejection.reason"</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-attribute"> headers</span>:
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> Content-Type</span>: application/json;charset=UTF-<span class="hl-number">8</span></pre><p>In the case of messaging, the input and the output messages can be defined (taking into account from and
|
||||
where to it was sent, the message body and header), as well as the methods that should be called after the message
|
||||
is received or the methods that, when called, should trigger a message.
|
||||
An example of a Camel messaging contract expressed in Groovy DSL whould look like this:</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>While, the same contract expressed in YAML would look as in the code below:</p><pre class="programlisting">label: some_label
|
||||
input:
|
||||
messageFrom: jms:delete
|
||||
messageBody:
|
||||
bookName: 'foo'
|
||||
messageHeaders:
|
||||
sample: header
|
||||
assertThat: bookWasDeleted()</pre><p>Then, add Spring Cloud Contract Verifier dependency and plugin to your build file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><plugin></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><version></span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></version></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><extensions></span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></extensions></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></plugin></span></pre><p>Now, running <code class="literal">./mvnw clean install</code> will cause tests that verify the application
|
||||
compliance with the added contracts to be automatically generated, by default under <code class="literal">org.springframework.cloud.contract.verifier.tests.</code>.</p><p>A sample auto-generated test for an HTTP contract would look the following way:</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>The sample above uses Spring’s <code class="literal">MockMvc</code> to run the tests. This is the default test mode for HTTP
|
||||
contracts, however also JAX-RX client and explicit HTTP invocations can be used as well (just change
|
||||
the <code class="literal">testMode</code> property of the plugin to <code class="literal">JAX-RS</code> or <code class="literal">EXPLICIT</code>.</p><p>Apart from the default JUnit, you can also use Spock tests, instead, by setting the plugin <code class="literal">testFramework</code>
|
||||
property to <code class="literal">Spock</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>You can now also generate WireMock scenarios based on the contracts, by including an order number followed by
|
||||
an underscore at the beginning of the contract file names.</p></td></tr></table></div><p>A sample auto-generated test in Spock for a messaging stub contract would look similar to this:</p><pre class="literallayout">[source,groovy,indent=0]</pre><pre class="screen">given:
|
||||
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
|
||||
\'\'\'{"bookName":"foo"}\'\'\',
|
||||
['sample': 'header']
|
||||
)
|
||||
|
||||
when:
|
||||
contractVerifierMessaging.send(inputMessage, 'jms:delete')
|
||||
|
||||
then:
|
||||
noExceptionThrown()
|
||||
bookWasDeleted()</pre><p>As the implementation of the functionalities described by the contracts is not yet present,
|
||||
the tests will fail.</p><p>To make them pass, the correct implementation of handling either HTTP requests or messages
|
||||
will have to be added. Also, a correct base test class for auto-generated tests needs to be added to the project.
|
||||
This class will be extended by all the auto-generated tests and it should contain all the setup
|
||||
necessary to run them (for example <code class="literal">RestAssuredMockMvc</code> controller setup or messaging test setup).</p><p>Once the implementation and the test base class are in place, the tests will pass, and both the application
|
||||
and the stub artifacts will be built and installed in the local Maven repository. Information about
|
||||
installing the stubs jar to the local repository will appear in the logs:</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>The changes can now be merged and both the application and the stub artifacts may be published in an online repository.</p><p><span class="strong"><strong>Docker Project</strong></span></p><p>In order to enable working with contracts while creating applications in non-JVM technologies,
|
||||
the <code class="literal">springcloud/spring-cloud-contract</code> Docker image has been created. It contains a project that will
|
||||
automatically generate tests for HTTP contracts and execute them in <code class="literal">EXPLICIT</code> test mode, then, if
|
||||
the tests pass, generate Wiremock stubs and -optionally- publish them to an artifact manager. In order to use the
|
||||
image, it’s sufficient to mount the contracts into the <code class="literal">/contracts</code> directory and set a few environment variables.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_on_the_consumer_side_2" href="#_on_the_consumer_side_2"></a>On the Consumer Side</h4></div></div></div><p><code class="literal">Spring Cloud Contract Stub Runner</code> can be used in the integration tests to get a running WireMock instance/
|
||||
messaging route that simulates the actual service.</p><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"><dependency></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>Get the Producer-side stubs installed in your Maven repository by either:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">checking out the Producer side repository, adding contracts and generating the stubs by running:</li></ul></div><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><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 tests are being skipped because the Producer-side contract implementation is not in place yet,
|
||||
so the automatically-generated contract tests would fail;</p></td></tr></table></div><p>or:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">getting already existing producer service stubs from a remote repository; to do this, simply pass the
|
||||
stub artifact ids and artifact repository url as <code class="literal">Spring Cloud Contract Stub Runner</code> properties:</li></ul></div><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>: http://repo.spring.io/libs-snapshot</pre><p>Now just annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation, provide
|
||||
the group-id and artifact-id for <code class="literal">Spring Cloud Contract Stub Runner</code> to run the collaborators' stubs for you.</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>Use the <code class="literal">REMOTE</code> stubsMode when downloading stubs from an online repository and <code class="literal">LOCAL</code> for offline work.</p></td></tr></table></div><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"},
|
||||
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</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 in your integration test, you will be able to receive stubbed versions of HTTP responses or messages that are
|
||||
expected to be emitted by the collaborator service. You will see entries similar to theses in the build logs:</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></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_defining_the_contract" href="#_defining_the_contract"></a>2.3.3 Defining the contract</h3></div></div></div><p>As consumers of services, we need to define what exactly we want to achieve. We need to
|
||||
formulate our expectations. That is why we write contracts.</p><p>Assume that you want to send a request containing the ID of a client company and the
|
||||
amount it wants to borrow from us. You also want to send it to the /fraudcheck url via
|
||||
the PUT method.</p><p><b>Groovy DSL. </b>
|
||||
@@ -138,7 +328,7 @@ response: # (7)
|
||||
#(9) - and JSON body equal to
|
||||
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
|
||||
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`</pre><p>
|
||||
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_client_side" href="#_client_side"></a>2.3.2 Client Side</h3></div></div></div><p>Spring Cloud Contract generates stubs, which you can use during client-side testing.
|
||||
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_client_side" href="#_client_side"></a>2.3.4 Client Side</h3></div></div></div><p>Spring Cloud Contract generates stubs, which you can use during client-side testing.
|
||||
You get a running WireMock instance/Messaging route that simulates the service.
|
||||
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<FraudServiceResponse> 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,
|
||||
@@ -150,7 +340,7 @@ You would like to feed that instance with a proper stub definition.</p><p>At som
|
||||
<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 automatically finds the stubs
|
||||
(simulating the real service) in the Maven repository and exposes them on a configured
|
||||
(or random) port.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_server_side" href="#_server_side"></a>2.3.3 Server Side</h3></div></div></div><p>Since you are developing your stub, you need to be sure that it actually resembles your
|
||||
(or random) port.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_server_side" href="#_server_side"></a>2.3.5 Server Side</h3></div></div></div><p>Since you are developing your stub, you need to be sure that it actually resembles your
|
||||
concrete implementation. You cannot have a situation where your stub acts in one way and
|
||||
your application behaves in a different way, especially in production.</p><p>To ensure that your application behaves the way you define in your stub, tests are
|
||||
generated from the stub you provide.</p><p>The autogenerated test looks, more or less, like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
||||
@@ -383,7 +573,7 @@ of an identifier or a timestamp, you need not hardcode a value. You want to allo
|
||||
different ranges of values. To enable ranges of values, you can set regular expressions
|
||||
matching those values for the consumer side. You can provide the body by means of either
|
||||
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
|
||||
<a class="link" href="https://cloud.spring.io/spring-cloud-contract/single/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>You must understand the map notation in order 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 previously shown 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 all of</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">a <code class="literal">PUT</code> method on the <code class="literal">/fraudcheck</code> endpoint,</li><li class="listitem">a JSON body with a <code class="literal">client.id</code> that matches 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 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><li class="listitem"><p class="simpara">then an HTTP response is 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 a JSON body with the <code class="literal">fraudCheckStatus</code> field containing a value <code class="literal">FRAUD</code> and
|
||||
@@ -441,7 +631,7 @@ Application service</code>):</p><p>Add the <code class="literal">Spring Cloud Co
|
||||
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>Annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation, provide the
|
||||
<code class="literal">group-id</code> and <code class="literal">artifact-id</code> for the Stub Runner to download the stubs of your
|
||||
collaborators. (Optional step) Because you’re playing with the collaborators offline, you
|
||||
can also provide the offline work switch.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
||||
can also provide the offline work switch (<code class="literal">StubRunnerProperties.StubsMode.LOCAL</code>).</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"},
|
||||
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</span></em>
|
||||
|
||||
Reference in New Issue
Block a user