1607 lines
62 KiB
HTML
1607 lines
62 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="generator" content="Asciidoctor 1.5.7.1">
|
|
<title>Spring Cloud Contract Verifier Introduction</title>
|
|
<link rel="stylesheet" href="css/spring.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
|
|
<style>
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.switch {
|
|
border-width: 1px 1px 0 1px;
|
|
border-style: solid;
|
|
border-color: #7a2518;
|
|
display: inline-block;
|
|
}
|
|
|
|
.switch--item {
|
|
padding: 10px;
|
|
background-color: #ffffff;
|
|
color: #7a2518;
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.switch--item:not(:first-child) {
|
|
border-width: 0 0 0 1px;
|
|
border-style: solid;
|
|
border-color: #7a2518;
|
|
}
|
|
|
|
.switch--item.selected {
|
|
background-color: #7a2519;
|
|
color: #ffffff;
|
|
}
|
|
</style>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
|
|
<script type="text/javascript">
|
|
function addBlockSwitches() {
|
|
$('.primary').each(function() {
|
|
primary = $(this);
|
|
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
|
|
primary.children('.title').remove();
|
|
});
|
|
$('.secondary').each(function(idx, node) {
|
|
secondary = $(node);
|
|
primary = findPrimary(secondary);
|
|
switchItem = createSwitchItem(secondary, primary.children('.switch'));
|
|
switchItem.content.addClass('hidden');
|
|
findPrimary(secondary).append(switchItem.content);
|
|
secondary.remove();
|
|
});
|
|
}
|
|
|
|
function createBlockSwitch(primary) {
|
|
blockSwitch = $('<div class="switch"></div>');
|
|
primary.prepend(blockSwitch);
|
|
return blockSwitch;
|
|
}
|
|
|
|
function findPrimary(secondary) {
|
|
candidate = secondary.prev();
|
|
while (!candidate.is('.primary')) {
|
|
candidate = candidate.prev();
|
|
}
|
|
return candidate;
|
|
}
|
|
|
|
function createSwitchItem(block, blockSwitch) {
|
|
blockName = block.children('.title').text();
|
|
content = block.children('.content').first().append(block.next('.colist'));
|
|
item = $('<div class="switch--item">' + blockName + '</div>');
|
|
item.on('click', '', content, function(e) {
|
|
$(this).addClass('selected');
|
|
$(this).siblings().removeClass('selected');
|
|
e.data.siblings('.content').addClass('hidden');
|
|
e.data.removeClass('hidden');
|
|
});
|
|
blockSwitch.append(item);
|
|
return {'item': item, 'content': content};
|
|
}
|
|
|
|
$(addBlockSwitches);
|
|
</script>
|
|
|
|
</head>
|
|
<body class="book toc2 toc-left">
|
|
<div id="header">
|
|
<div id="toc" class="toc2">
|
|
<div id="toctitle">Table of Contents</div>
|
|
<ul class="sectlevel1">
|
|
<li><a href="#_spring_cloud_contract_verifier_introduction">Spring Cloud Contract Verifier Introduction</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#_history">History</a></li>
|
|
<li><a href="#_why_a_contract_verifier">Why a Contract Verifier?</a></li>
|
|
<li><a href="#_purposes">Purposes</a></li>
|
|
<li><a href="#_how_it_works">How It Works</a></li>
|
|
<li><a href="#_step_by_step_guide_to_consumer_driven_contracts_cdc">Step-by-step Guide to Consumer Driven Contracts (CDC)</a></li>
|
|
<li><a href="#_dependencies">Dependencies</a></li>
|
|
<li><a href="#_additional_links">Additional Links</a></li>
|
|
<li><a href="#_samples">Samples</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div id="content">
|
|
<div class="sect1">
|
|
<h2 id="_spring_cloud_contract_verifier_introduction"><a class="link" href="#_spring_cloud_contract_verifier_introduction">Spring Cloud Contract Verifier Introduction</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Contract Verifier enables Consumer Driven Contract (CDC) development of
|
|
JVM-based applications. It moves TDD to the level of software architecture.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Contract Verifier ships with <em>Contract Definition Language</em> (CDL). Contract
|
|
definitions are used to produce the following resources:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>JSON stub definitions to be used by WireMock when doing integration testing on the
|
|
client code (<em>client tests</em>). Test code must still be written by hand, and test data is
|
|
produced by Spring Cloud Contract Verifier.</p>
|
|
</li>
|
|
<li>
|
|
<p>Messaging routes, if you’re using a messaging service. We integrate with Spring
|
|
Integration, Spring Cloud Stream, Spring AMQP, and Apache Camel. You can also set your
|
|
own integrations.</p>
|
|
</li>
|
|
<li>
|
|
<p>Acceptance tests (in JUnit 4, JUnit 5, TestNG or Spock) are used to verify if server-side implementation
|
|
of the API is compliant with the contract (<em>server tests</em>). A full test is generated by
|
|
Spring Cloud Contract Verifier.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_history"><a class="link" href="#_history">History</a></h3>
|
|
<div class="paragraph">
|
|
<p>Before becoming Spring Cloud Contract, this project was called <a href="https://github.com/Codearte/accurest">Accurest</a>.
|
|
It was created by <a href="https://twitter.com/mgrzejszczak">Marcin Grzejszczak</a> and <a href="https://twitter.com/jkubrynski">Jakub Kubrynski</a>
|
|
from (<a href="https://github.com/Codearte">Codearte</a>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The <code>0.1.0</code> release took place on 26 Jan 2015 and it became stable with <code>1.0.0</code> release on 29 Feb 2016.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_why_a_contract_verifier"><a class="link" href="#_why_a_contract_verifier">Why a Contract Verifier?</a></h3>
|
|
<div class="paragraph">
|
|
<p>Assume that we have a system consisting of multiple microservices:</p>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_testing_issues"><a class="link" href="#_testing_issues">Testing issues</a></h4>
|
|
<div class="paragraph">
|
|
<p>If we wanted to test the application in top left corner to determine whether it can
|
|
communicate with other services, we could do one of two things:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>Deploy all microservices and perform end-to-end tests.</p>
|
|
</li>
|
|
<li>
|
|
<p>Mock other microservices in unit/integration tests.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Both have their advantages but also a lot of disadvantages.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Deploy all microservices and perform end to end tests</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Advantages:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>Simulates production.</p>
|
|
</li>
|
|
<li>
|
|
<p>Tests real communication between services.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Disadvantages:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>To test one microservice, we have to deploy 6 microservices, a couple of databases,
|
|
etc.</p>
|
|
</li>
|
|
<li>
|
|
<p>The environment where the tests run is locked for a single suite of tests (nobody else
|
|
would be able to run the tests in the meantime).</p>
|
|
</li>
|
|
<li>
|
|
<p>They take a long time to run.</p>
|
|
</li>
|
|
<li>
|
|
<p>The feedback comes very late in the process.</p>
|
|
</li>
|
|
<li>
|
|
<p>They are extremely hard to debug.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Mock other microservices in unit/integration tests</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Advantages:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>They provide very fast feedback.</p>
|
|
</li>
|
|
<li>
|
|
<p>They have no infrastructure requirements.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Disadvantages:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>The implementor of the service creates stubs that might have nothing to do with
|
|
reality.</p>
|
|
</li>
|
|
<li>
|
|
<p>You can go to production with passing tests and failing production.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To solve the aforementioned issues, Spring Cloud Contract Verifier with Stub Runner was
|
|
created. The 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 directly uses.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Contract Verifier gives you the certainty that the stubs that you use were
|
|
created by the service that you’re calling. Also, if you can use them, it means that they
|
|
were tested against the producer’s side. In short, you can trust those stubs.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_purposes"><a class="link" href="#_purposes">Purposes</a></h3>
|
|
<div class="paragraph">
|
|
<p>The main purposes of Spring Cloud Contract Verifier with Stub Runner are:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>To ensure that WireMock/Messaging stubs (used when developing the client) do exactly
|
|
what the actual server-side implementation does.</p>
|
|
</li>
|
|
<li>
|
|
<p>To promote ATDD method and Microservices architectural style.</p>
|
|
</li>
|
|
<li>
|
|
<p>To provide a way to publish changes in contracts that are immediately visible on both
|
|
sides.</p>
|
|
</li>
|
|
<li>
|
|
<p>To generate boilerplate test code to be used on the server side.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Spring Cloud Contract Verifier’s purpose is NOT to start writing business
|
|
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.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_how_it_works"><a class="link" href="#_how_it_works">How It Works</a></h3>
|
|
<div class="paragraph">
|
|
<p>This section explores how Spring Cloud Contract Verifier with Stub Runner works.</p>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-cloud-contract-verifier-intro-three-second-tour"><a class="link" href="#spring-cloud-contract-verifier-intro-three-second-tour">A Three-second Tour</a></h4>
|
|
<div class="paragraph">
|
|
<p>This very brief tour walks through using Spring Cloud Contract:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><a href="#spring-cloud-contract-verifier-intro-three-second-tour-producer">On the Producer Side</a></p>
|
|
</li>
|
|
<li>
|
|
<p><a href="#spring-cloud-contract-verifier-intro-three-second-tour-consumer">On the Consumer Side</a></p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can find a somewhat longer tour
|
|
<a href="#spring-cloud-contract-verifier-intro-three-minute-tour">here</a>.</p>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="spring-cloud-contract-verifier-intro-three-second-tour-producer"><a class="link" href="#spring-cloud-contract-verifier-intro-three-second-tour-producer">On the Producer Side</a></h5>
|
|
<div class="paragraph">
|
|
<p>To start working with Spring Cloud Contract, add files with <code>REST/</code> messaging contracts
|
|
expressed in either Groovy DSL or YAML to the contracts directory, which is set by the
|
|
<code>contractsDslDir</code> property. By default, it is <code>$rootDir/src/test/resources/contracts</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Then add the Spring Cloud Contract Verifier dependency and plugin to your build file, as
|
|
shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following listing shows how to add the plugin, which should go in the build/plugins
|
|
portion of the file:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><plugin>
|
|
<groupId>org.springframework.cloud</groupId>
|
|
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
|
<version>${spring-cloud-contract.version}</version>
|
|
<extensions>true</extensions>
|
|
</plugin></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Running <code>./mvnw clean install</code> automatically generates tests that verify the application
|
|
compliance with the added contracts. By default, the tests get generated under
|
|
<code>org.springframework.cloud.contract.verifier.tests.</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>As the implementation of the functionalities described by the contracts is not yet
|
|
present, the tests fail.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To make them pass, you must add the correct implementation of either handling HTTP
|
|
requests or messages. Also, you must add a correct base test class for auto-generated
|
|
tests to the project. This class is extended by all the auto-generated tests, and it
|
|
should contain all the setup necessary to run them (for example <code>RestAssuredMockMvc</code>
|
|
controller setup or messaging test setup).</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Once the implementation and the test base class are in place, the tests pass, and both the
|
|
application and the stub artifacts are 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>
|
|
<div class="sect4">
|
|
<h5 id="spring-cloud-contract-verifier-intro-three-second-tour-consumer"><a class="link" href="#spring-cloud-contract-verifier-intro-three-second-tour-consumer">On the Consumer Side</a></h5>
|
|
<div class="paragraph">
|
|
<p><code>Spring Cloud Contract Stub Runner</code> can be used in the integration tests to get a running
|
|
WireMock instance or messaging route that simulates the actual service.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To do so, add the dependency to <code>Spring Cloud Contract Stub Runner</code>, as shown in the
|
|
following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can get the Producer-side stubs installed in your Maven repository in either of two
|
|
ways:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>By checking out the Producer side repository and adding contracts and generating the stubs
|
|
by running the following commands:</p>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ cd local-http-server-repo
|
|
$ ./mvnw clean install -DskipTests</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
The tests are being skipped because the Producer-side contract implementation is not
|
|
in place yet, so the automatically-generated contract tests fail.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>By getting already-existing producer service stubs from a remote repository. To do so,
|
|
pass the stub artifact IDs and artifact repository URL as <code>Spring Cloud Contract
|
|
Stub Runner</code> properties, as shown in the following example:</p>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml"></code></pre>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now you can annotate your test class with <code>@AutoConfigureStubRunner</code>. In the annotation,
|
|
provide the <code>group-id</code> and <code>artifact-id</code> values for <code>Spring Cloud Contract Stub Runner</code> to
|
|
run the collaborators' stubs for you, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
|
|
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
|
|
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
|
|
public class LoanApplicationServiceTests {</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
Use the <code>REMOTE</code> <code>stubsMode</code> when downloading stubs from an online repository and
|
|
<code>LOCAL</code> for offline work.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now, in your integration test, you can receive stubbed versions of HTTP responses or
|
|
messages that are expected to be emitted by the collaborator service.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-cloud-contract-verifier-intro-three-minute-tour"><a class="link" href="#spring-cloud-contract-verifier-intro-three-minute-tour">A Three-minute Tour</a></h4>
|
|
<div class="paragraph">
|
|
<p>This brief tour walks through using Spring Cloud Contract:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><a href="#spring-cloud-contract-verifier-intro-three-minute-tour-producer">On the Producer Side</a></p>
|
|
</li>
|
|
<li>
|
|
<p><a href="#spring-cloud-contract-verifier-intro-three-minute-tour-consumer">On the Consumer Side</a></p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can find an even more brief tour
|
|
<a href="#spring-cloud-contract-verifier-intro-three-second-tour">here</a>.</p>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="spring-cloud-contract-verifier-intro-three-minute-tour-producer"><a class="link" href="#spring-cloud-contract-verifier-intro-three-minute-tour-producer">On the Producer Side</a></h5>
|
|
<div class="paragraph">
|
|
<p>To start working with <code>Spring Cloud Contract</code>, add files with <code>REST/</code> messaging contracts
|
|
expressed in either Groovy DSL or YAML to the contracts directory, which is set by the
|
|
<code>contractsDslDir</code> property. By default, it is <code>$rootDir/src/test/resources/contracts</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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, and so
|
|
on). The following example shows how an HTTP stub contract in Groovy DSL:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">package contracts
|
|
|
|
org.springframework.cloud.contract.spec.Contract.make {
|
|
request {
|
|
method 'PUT'
|
|
url '/fraudcheck'
|
|
body([
|
|
"client.id": $(regex('[0-9]{10}')),
|
|
loanAmount: 99999
|
|
])
|
|
headers {
|
|
contentType('application/json')
|
|
}
|
|
}
|
|
response {
|
|
status OK()
|
|
body([
|
|
fraudCheckStatus: "FRAUD",
|
|
"rejection.reason": "Amount too high"
|
|
])
|
|
headers {
|
|
contentType('application/json')
|
|
}
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The same contract expressed in YAML would look like the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">request:
|
|
method: PUT
|
|
url: /fraudcheck
|
|
body:
|
|
"client.id": 1234567890
|
|
loanAmount: 99999
|
|
headers:
|
|
Content-Type: application/json
|
|
matchers:
|
|
body:
|
|
- path: $.['client.id']
|
|
type: by_regex
|
|
value: "[0-9]{10}"
|
|
response:
|
|
status: 200
|
|
body:
|
|
fraudCheckStatus: "FRAUD"
|
|
"rejection.reason": "Amount too high"
|
|
headers:
|
|
Content-Type: application/json;charset=UTF-8</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In the case of messaging, you can define:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>The input and the output messages can be defined (taking into account from and where it
|
|
was sent, the message body, and the header).</p>
|
|
</li>
|
|
<li>
|
|
<p>The methods that should be called after the message is received.</p>
|
|
</li>
|
|
<li>
|
|
<p>The methods that, when called, should trigger a message.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows a Camel messaging contract expressed in Groovy DSL:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows the same contract expressed in YAML:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yml hljs" data-lang="yml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file,
|
|
as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following listing shows how to add the plugin, which should go in the build/plugins
|
|
portion of the file:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><plugin>
|
|
<groupId>org.springframework.cloud</groupId>
|
|
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
|
<version>${spring-cloud-contract.version}</version>
|
|
<extensions>true</extensions>
|
|
</plugin></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Running <code>./mvnw clean install</code> automatically generates tests that verify the application
|
|
compliance with the added contracts. By default, the generated tests are under
|
|
<code>org.springframework.cloud.contract.verifier.tests.</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows a sample auto-generated test for an HTTP contract:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Test
|
|
public void validate_shouldMarkClientAsFraud() throws Exception {
|
|
// given:
|
|
MockMvcRequestSpecification request = given()
|
|
.header("Content-Type", "application/vnd.fraud.v1+json")
|
|
.body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
|
|
|
|
// when:
|
|
ResponseOptions response = given().spec(request)
|
|
.put("/fraudcheck");
|
|
|
|
// 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("['rejection.reason']").isEqualTo("Amount too high");
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The preceding example uses Spring’s <code>MockMvc</code> to run the tests. This is the default test
|
|
mode for HTTP contracts. However, JAX-RS client and explicit HTTP invocations can also be
|
|
used. (To do so, change the <code>testMode</code> property of the plugin to <code>JAX-RS</code> or <code>EXPLICIT</code>,
|
|
respectively.)</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Since 2.1.0, it is also possible to use <code>RestAssuredWebTestClient`with Spring’s reactive `WebTestClient</code>
|
|
run under the hood. This is particularly recommended while working with Reactive, <code>Web-Flux</code>-based applications.
|
|
In order to use <code>WebTestClient</code> set <code>testMode</code> to <code>WEBTESTCLIENT</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Here is an example of a test generated in <code>WEBTESTCLIENT</code> test mode:</p>
|
|
</div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre>[source,java,indent=0]</pre>
|
|
</div>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre>@Test
|
|
public void validate_shouldRejectABeerIfTooYoung() throws Exception {
|
|
// given:
|
|
WebTestClientRequestSpecification request = given()
|
|
.header("Content-Type", "application/json")
|
|
.body("{\"age\":10}");
|
|
|
|
// when:
|
|
WebTestClientResponse response = given().spec(request)
|
|
.post("/check");
|
|
|
|
// then:
|
|
assertThat(response.statusCode()).isEqualTo(200);
|
|
assertThat(response.header("Content-Type")).matches("application/json.*");
|
|
// and:
|
|
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
|
assertThatJson(parsedJson).field("['status']").isEqualTo("NOT_OK");
|
|
}</pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Apart from the default JUnit 4, you can instead use JUnit 5, TestNG or Spock tests, by setting the plugin
|
|
<code>testFramework</code> property to either <code>JUNIT5</code>, <code>TESTNG</code> or <code>Spock</code>.</p>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
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.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows an auto-generated test in Spock for a messaging stub contract:</p>
|
|
</div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre>[source,groovy,indent=0]</pre>
|
|
</div>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre>given:
|
|
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
|
|
\'\'\'{"bookName":"foo"}\'\'\',
|
|
['sample': 'header']
|
|
)
|
|
|
|
when:
|
|
contractVerifierMessaging.send(inputMessage, 'jms:delete')
|
|
|
|
then:
|
|
noExceptionThrown()
|
|
bookWasDeleted()</pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>As the implementation of the functionalities described by the contracts is not yet
|
|
present, the tests fail.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To make them pass, you must add the correct implementation of handling either HTTP
|
|
requests or messages. Also, you must add a correct base test class for auto-generated
|
|
tests to the project. This class is extended by all the auto-generated tests and should
|
|
contain all the setup necessary to run them (for example, <code>RestAssuredMockMvc</code> controller
|
|
setup or messaging test setup).</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Once the implementation and the test base class are in place, the tests pass, and both the
|
|
application and the stub artifacts are built and installed in the local Maven repository.
|
|
Information about installing the stubs jar to the local repository appears in the logs, as
|
|
shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" 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.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
|
|
[INFO]
|
|
[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>You can now merge the changes and publish both the application and the stub artifacts
|
|
in an online repository.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Docker Project</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In order to enable working with contracts while creating applications in non-JVM
|
|
technologies, the <code>springcloud/spring-cloud-contract</code> Docker image has been created. It
|
|
contains a project that automatically generates tests for HTTP contracts and executes them
|
|
in <code>EXPLICIT</code> test mode. Then, if the tests pass, it generates Wiremock stubs and,
|
|
optionally, publishes them to an artifact manager. In order to use the image, you can
|
|
mount the contracts into the <code>/contracts</code> directory and set a few environment variables.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="spring-cloud-contract-verifier-intro-three-minute-tour-consumer"><a class="link" href="#spring-cloud-contract-verifier-intro-three-minute-tour-consumer">On the Consumer Side</a></h5>
|
|
<div class="paragraph">
|
|
<p><code>Spring Cloud Contract Stub Runner</code> can be used in the integration tests to get a running
|
|
WireMock instance or messaging route that simulates the actual service.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To get started, add the dependency to <code>Spring Cloud Contract Stub Runner</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can get the Producer-side stubs installed in your Maven repository in either of two
|
|
ways:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>By checking out the Producer side repository and adding contracts and generating the
|
|
stubs by running the following commands:</p>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ cd local-http-server-repo
|
|
$ ./mvnw clean install -DskipTests</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
The tests are skipped because the Producer-side contract implementation is not yet
|
|
in place, so the automatically-generated contract tests fail.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Getting already existing producer service stubs from a remote repository. To do so,
|
|
pass the stub artifact IDs and artifact repository URl as <code>Spring Cloud Contract Stub
|
|
Runner</code> properties, as shown in the following example:</p>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml"></code></pre>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now you can annotate your test class with <code>@AutoConfigureStubRunner</code>. In the annotation,
|
|
provide the <code>group-id</code> and <code>artifact-id</code> for <code>Spring Cloud Contract Stub Runner</code> to run
|
|
the collaborators' stubs for you, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
|
|
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
|
|
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
|
|
public class LoanApplicationServiceTests {</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
Use the <code>REMOTE</code> <code>stubsMode</code> when downloading stubs from an online repository and
|
|
<code>LOCAL</code> for offline work.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In your integration test, you can receive stubbed versions of HTTP responses or messages
|
|
that are expected to be emitted by the collaborator service. You can see entries similar
|
|
to the following in the build logs:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">2016-07-19 14:22:25.403 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Desired version is + - will try to resolve the latest version
|
|
2016-07-19 14:22:25.438 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved version is 0.0.1-SNAPSHOT
|
|
2016-07-19 14:22:25.439 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories []
|
|
2016-07-19 14:22:25.451 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
|
|
2016-07-19 14:22:25.465 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar]
|
|
2016-07-19 14:22:25.475 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
|
|
2016-07-19 14:22:27.737 INFO 41050 --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_defining_the_contract"><a class="link" href="#_defining_the_contract">Defining the Contract</a></h4>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="title">Groovy DSL</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="title">YAML</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yml hljs" data-lang="yml"></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_client_side"><a class="link" href="#_client_side">Client Side</a></h4>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>At some point in time, you need to send a request to the Fraud Detection service.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Annotate your test class with <code>@AutoConfigureStubRunner</code>. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
<div class="sect3">
|
|
<h4 id="_server_side"><a class="link" href="#_server_side">Server Side</a></h4>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To ensure that your application behaves the way you define in your stub, tests are
|
|
generated from the stub you provide.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The autogenerated test looks, more or less, like this:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Test
|
|
public void validate_shouldMarkClientAsFraud() throws Exception {
|
|
// given:
|
|
MockMvcRequestSpecification request = given()
|
|
.header("Content-Type", "application/vnd.fraud.v1+json")
|
|
.body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
|
|
|
|
// when:
|
|
ResponseOptions response = given().spec(request)
|
|
.put("/fraudcheck");
|
|
|
|
// 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("['rejection.reason']").isEqualTo("Amount too high");
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_step_by_step_guide_to_consumer_driven_contracts_cdc"><a class="link" href="#_step_by_step_guide_to_consumer_driven_contracts_cdc">Step-by-step Guide to Consumer Driven Contracts (CDC)</a></h3>
|
|
<div class="paragraph">
|
|
<p>Consider an example of Fraud Detection and the Loan Issuance process. The business
|
|
scenario is such that we want to issue loans to people but do not want them to steal from
|
|
us. The current implementation of our system grants loans to everybody.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Assume that <code>Loan Issuance</code> is a client to the <code>Fraud Detection</code> server. In the current
|
|
sprint, we must develop a new feature: if a client wants to borrow too much money, then
|
|
we mark the client as a fraud.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Technical remark - Fraud Detection has an <code>artifact-id</code> of <code>http-server</code>, while Loan
|
|
Issuance has an artifact-id of <code>http-client</code>, and both have a <code>group-id</code> of <code>com.example</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples/standalone/dsl/http-server">server
|
|
side code is available here</a> and <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples/standalone/dsl/http-client">the
|
|
client code here</a>.</p>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
In this case, the producer owns the contracts. Physically, all the contract are
|
|
in the producer’s repository.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_technical_note"><a class="link" href="#_technical_note">Technical note</a></h4>
|
|
<div class="paragraph">
|
|
<p>If using the <strong>SNAPSHOT</strong> / <strong>Milestone</strong> / <strong>Release Candidate</strong> versions please add the
|
|
following section to your build:</p>
|
|
</div>
|
|
<div class="listingblock primary">
|
|
<div class="title">Maven</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="listingblock secondary">
|
|
<div class="title">Gradle</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_consumer_side_loan_issuance"><a class="link" href="#_consumer_side_loan_issuance">Consumer side (Loan Issuance)</a></h4>
|
|
<div class="paragraph">
|
|
<p>As a developer of the Loan Issuance service (a consumer of the Fraud Detection server), you might do the following steps:</p>
|
|
</div>
|
|
<div class="olist arabic">
|
|
<ol class="arabic">
|
|
<li>
|
|
<p>Start doing TDD by writing a test for your feature.</p>
|
|
</li>
|
|
<li>
|
|
<p>Write the missing implementation.</p>
|
|
</li>
|
|
<li>
|
|
<p>Clone the Fraud Detection service repository locally.</p>
|
|
</li>
|
|
<li>
|
|
<p>Define the contract locally in the repo of Fraud Detection service.</p>
|
|
</li>
|
|
<li>
|
|
<p>Add the Spring Cloud Contract Verifier plugin.</p>
|
|
</li>
|
|
<li>
|
|
<p>Run the integration tests.</p>
|
|
</li>
|
|
<li>
|
|
<p>File a pull request.</p>
|
|
</li>
|
|
<li>
|
|
<p>Create an initial implementation.</p>
|
|
</li>
|
|
<li>
|
|
<p>Take over the pull request.</p>
|
|
</li>
|
|
<li>
|
|
<p>Write the missing implementation.</p>
|
|
</li>
|
|
<li>
|
|
<p>Deploy your app.</p>
|
|
</li>
|
|
<li>
|
|
<p>Work online.</p>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Start doing TDD by writing a test for your feature.</strong></p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Assume that you have written a test of your new feature. If a loan application for a big
|
|
amount is received, the system should reject that loan application with some description.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Write the missing implementation.</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>At some point in time, you need to send a request to the Fraud Detection service. Assume
|
|
that you need to send the request containing the ID of the client and the amount the
|
|
client wants to borrow. You want to send it to the <code>/fraudcheck</code> url via the <code>PUT</code> method.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For simplicity, the port of the Fraud Detection service is set to <code>8080</code>, and the
|
|
application runs on <code>8090</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you start the test at this point, it breaks, because no service currently runs on port
|
|
<code>8080</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Clone the Fraud Detection service repository locally.</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can start by playing around with the server side contract. To do so, you must first
|
|
clone it.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ git clone https://your-git-server.com/server-side.git local-http-server-repo</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Define the contract locally in the repo of Fraud Detection service.</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>As a consumer, you need to define what exactly you want to achieve. You need to formulate
|
|
your expectations. To do so, write the following contract:</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Place the contract under <code>src/test/resources/contracts/fraud</code> folder. The <code>fraud</code> folder
|
|
is important because the producer’s test base class name references that folder.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="title">Groovy DSL</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="title">YAML</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yml hljs" data-lang="yml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The YML contract is quite straight-forward. However when you take a look at the Contract
|
|
written using a statically typed Groovy DSL - you might wonder what the
|
|
<code>value(client(…​), server(…​))</code> parts are. By using this notation, Spring Cloud
|
|
Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case
|
|
of an identifier or a timestamp, you need not hardcode a value. You want to allow some
|
|
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.
|
|
Consult the <a href="#contract-dsl">[contract-dsl]</a> section for more information. We highly recommend using the map notation!</p>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
You must understand the map notation in order to set up contracts. Please read the
|
|
<a href="https://groovy-lang.org/json.html">Groovy docs regarding JSON</a>.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The previously shown contract is an agreement between two sides that:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>if an HTTP request is sent with all of</p>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>a <code>PUT</code> method on the <code>/fraudcheck</code> endpoint,</p>
|
|
</li>
|
|
<li>
|
|
<p>a JSON body with a <code>client.id</code> that matches the regular expression <code>[0-9]{10}</code> and
|
|
<code>loanAmount</code> equal to <code>99999</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>
|
|
<li>
|
|
<p>then an HTTP response is sent to the consumer that</p>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>has status <code>200</code>,</p>
|
|
</li>
|
|
<li>
|
|
<p>contains a 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 you are ready to check the API in practice in the integration tests, you need to
|
|
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 a Maven or a Gradle plugin. In this example, you see how to add Maven.
|
|
First, add the <code>Spring Cloud Contract</code> BOM.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Next, add the <code>Spring Cloud Contract Verifier</code> Maven plugin</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Since the plugin was added, you 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>You do not want to generate tests since you, as the consumer, want only to play with the
|
|
stubs. You need to skip the test generation and execution. When you execute:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ cd local-http-server-repo
|
|
$ ./mvnw clean install -DskipTests</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In the logs, you see something like this:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" 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.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
|
|
[INFO]
|
|
[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>The following line is extremely important:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" 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>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>It confirms 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 must do the following in your consumer side project (<code>Loan
|
|
Application service</code>):</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Add the <code>Spring Cloud Contract</code> BOM:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Add the dependency to <code>Spring Cloud Contract Stub Runner</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Annotate your test class with <code>@AutoConfigureStubRunner</code>. In the annotation, provide the
|
|
<code>group-id</code> and <code>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 (<code>StubRunnerProperties.StubsMode.LOCAL</code>).</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now, when you run your tests, you see something like this:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">2016-07-19 14:22:25.403 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Desired version is + - will try to resolve the latest version
|
|
2016-07-19 14:22:25.438 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved version is 0.0.1-SNAPSHOT
|
|
2016-07-19 14:22:25.439 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories []
|
|
2016-07-19 14:22:25.451 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
|
|
2016-07-19 14:22:25.465 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar]
|
|
2016-07-19 14:22:25.475 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
|
|
2016-07-19 14:22:27.737 INFO 41050 --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>This output means that Stub Runner has found your stubs and started a server for your app
|
|
with group id <code>com.example</code>, artifact id <code>http-server</code> with version <code>0.0.1-SNAPSHOT</code> of
|
|
the stubs and with <code>stubs</code> classifier on port <code>8080</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>File a pull request.</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>What you have done until now is an iterative process. You can play around with the
|
|
contract, install it locally, and work on the consumer side until the contract works as
|
|
you wish.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Once you are satisfied with the results and the test passes, publish a pull request to
|
|
the server side. Currently, the consumer side work is done.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_producer_side_fraud_detection_server"><a class="link" href="#_producer_side_fraud_detection_server">Producer side (Fraud Detection server)</a></h4>
|
|
<div class="paragraph">
|
|
<p>As a developer of the Fraud Detection server (a server to the Loan Issuance service):</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Create an initial implementation.</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>As a reminder, you can see the initial implementation here:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Take over the pull request.</strong></p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ git checkout -b contract-change-pr master
|
|
$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You must add the dependencies needed by the autogenerated tests:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In the configuration of the Maven plugin, pass the <code>packageWithBaseClasses</code> property</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
This example uses "convention based" naming by setting the
|
|
<code>packageWithBaseClasses</code> property. Doing so means that the two last packages combine to
|
|
make the name of the base test class. In our case, the contracts were placed under
|
|
<code>src/test/resources/contracts/fraud</code>. Since you do not have two packages starting from
|
|
the <code>contracts</code> folder, pick only one, which should be <code>fraud</code>. Add the <code>Base</code> suffix and
|
|
capitalize <code>fraud</code>. That gives you the <code>FraudBase</code> test class name.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>All the generated tests extend that class. Over there, you can set up your Spring Context
|
|
or whatever is necessary. In this case, use <a href="https://github.com/rest-assured/rest-assured">Rest Assured MVC</a> to
|
|
start the server side <code>FraudDetectionController</code>.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now, if you run the <code>./mvnw clean install</code>, you get something like this:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">Results :
|
|
|
|
Tests in error:
|
|
ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed...</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>This error occurs because you have a new contract from which a test was generated and it
|
|
failed since you have not implemented the feature. The auto-generated test would look
|
|
like this:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Test
|
|
public void validate_shouldMarkClientAsFraud() throws Exception {
|
|
// given:
|
|
MockMvcRequestSpecification request = given()
|
|
.header("Content-Type", "application/vnd.fraud.v1+json")
|
|
.body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
|
|
|
|
// when:
|
|
ResponseOptions response = given().spec(request)
|
|
.put("/fraudcheck");
|
|
|
|
// 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("['rejection.reason']").isEqualTo("Amount too high");
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you used the Groovy DSL, you can see, all the <code>producer()</code> parts of the Contract that were present in the
|
|
<code>value(consumer(…​), producer(…​))</code> blocks got injected into the test.
|
|
In case of using YAML, the same applied for the <code>matchers</code> sections of the <code>response</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Note that, on the producer side, you are also doing TDD. The expectations are expressed
|
|
in the form of a test. This test sends a request to our own application with the URL,
|
|
headers, and body defined in the contract. It also is expecting precisely defined values
|
|
in the response. In other words, you have the <code>red</code> part of <code>red</code>, <code>green</code>, and
|
|
<code>refactor</code>. It is 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>Because you know the expected input and expected output, you can write the missing
|
|
implementation:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>When you execute <code>./mvnw clean install</code> again, the tests 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 finish your work, you can deploy your change. First, merge the branch:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ git checkout master
|
|
$ git merge --no-ff contract-change-pr
|
|
$ git push origin master</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Your CI might run something like <code>./mvnw clean deploy</code>, which would publish both the
|
|
application and the stub artifacts.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_consumer_side_loan_issuance_final_step"><a class="link" href="#_consumer_side_loan_issuance_final_step">Consumer Side (Loan Issuance) Final Step</a></h4>
|
|
<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>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ git checkout master
|
|
$ git merge --no-ff contract-change-pr</code></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 and indicate
|
|
where the repository with your stubs is located. At this moment the stubs of the server
|
|
side are automatically downloaded from Nexus/Artifactory. You can set the value of
|
|
<code>stubsMode</code> to <code>REMOTE</code>. The following code shows an example of
|
|
achieving the same thing by changing the properties.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>That’s it!</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_dependencies"><a class="link" href="#_dependencies">Dependencies</a></h3>
|
|
<div class="paragraph">
|
|
<p>The best way to add dependencies is to use the proper <code>starter</code> dependency.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For <code>stub-runner</code>, use <code>spring-cloud-starter-stub-runner</code>. When you use a plugin, add
|
|
<code>spring-cloud-starter-contract-verifier</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_additional_links"><a class="link" href="#_additional_links">Additional Links</a></h3>
|
|
<div class="paragraph">
|
|
<p>Here are some resources related to Spring Cloud Contract Verifier and Stub Runner. Note
|
|
that some may be outdated, because the Spring Cloud Contract Verifier project is under
|
|
constant development.</p>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_spring_cloud_contract_video"><a class="link" href="#_spring_cloud_contract_video">Spring Cloud Contract video</a></h4>
|
|
<div class="paragraph">
|
|
<p>You can check out the video from the Warsaw JUG about Spring Cloud Contract:</p>
|
|
</div>
|
|
<div class="videoblock">
|
|
<div class="content">
|
|
<iframe width="640" height="480" src="https://www.youtube.com/embed/sAAklvxmPmk?rel=0&start=538" frameborder="0" allowfullscreen></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_readings"><a class="link" href="#_readings">Readings</a></h4>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><a href="https://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura">Slides from Marcin Grzejszczak’s talk about Accurest</a></p>
|
|
</li>
|
|
<li>
|
|
<p><a href="https://toomuchcoding.com/blog/categories/accurest/">Accurest related articles from Marcin Grzejszczak’s blog</a></p>
|
|
</li>
|
|
<li>
|
|
<p><a href="https://toomuchcoding.com/blog/categories/spring-cloud-contract/">Spring Cloud Contract related articles from Marcin Grzejszczak’s blog</a></p>
|
|
</li>
|
|
<li>
|
|
<p><a href="https://groovy-lang.org/json.html">Groovy docs regarding JSON</a></p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_samples"><a class="link" href="#_samples">Samples</a></h3>
|
|
<div class="paragraph">
|
|
<p>You can find some samples at
|
|
<a href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples">samples</a>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript" src="js/tocbot/tocbot.min.js"></script>
|
|
<script type="text/javascript" src="js/toc.js"></script>
|
|
<link rel="stylesheet" href="js/highlight/styles/atom-one-dark-reasonable.min.css">
|
|
<script src="js/highlight/highlight.min.js"></script>
|
|
<script>hljs.initHighlighting()</script>
|
|
</body>
|
|
</html> |