2993 lines
112 KiB
HTML
2993 lines
112 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>Contract DSL</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="#contract-dsl">Contract DSL</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_limitations">Limitations</a></li>
|
||
<li><a href="#_common_top_level_elements">Common Top-Level elements</a></li>
|
||
<li><a href="#_request">Request</a></li>
|
||
<li><a href="#_response">Response</a></li>
|
||
<li><a href="#_dynamic_properties">Dynamic properties</a></li>
|
||
<li><a href="#_jax_rs_support">JAX-RS Support</a></li>
|
||
<li><a href="#_async_support">Async Support</a></li>
|
||
<li><a href="#_working_with_context_paths">Working with Context Paths</a></li>
|
||
<li><a href="#_working_with_webflux">Working with WebFlux</a></li>
|
||
<li><a href="#_xml_support_for_rest">XML Support for REST</a></li>
|
||
<li><a href="#_messaging_top_level_elements">Messaging Top-Level Elements</a></li>
|
||
<li><a href="#_multiple_contracts_in_one_file">Multiple Contracts in One File</a></li>
|
||
<li><a href="#_generating_spring_rest_docs_snippets_from_the_contracts">Generating Spring REST Docs snippets from the contracts</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_customization">Customization</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_extending_the_dsl">Extending the DSL</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_using_the_pluggable_architecture">Using the Pluggable Architecture</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_custom_contract_converter">Custom Contract Converter</a></li>
|
||
<li><a href="#_using_the_custom_test_generator">Using the Custom Test Generator</a></li>
|
||
<li><a href="#_using_the_custom_stub_generator">Using the Custom Stub Generator</a></li>
|
||
<li><a href="#_using_the_custom_stub_runner">Using the Custom Stub Runner</a></li>
|
||
<li><a href="#_using_the_custom_stub_downloader">Using the Custom Stub Downloader</a></li>
|
||
<li><a href="#scm-stub-downloader">Using the SCM Stub Downloader</a></li>
|
||
<li><a href="#pact-stub-downloader">Using the Pact Stub Downloader</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div id="content">
|
||
<div class="sect1">
|
||
<h2 id="contract-dsl"><a class="link" href="#contract-dsl">Contract DSL</a></h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Spring Cloud Contract supports out of the box 2 types of DSL. One written in
|
||
<code>Groovy</code> and one written in <code>YAML</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy
|
||
before. Knowledge of the language is not really needed, as the Contract DSL uses only a
|
||
tiny subset of it (only literals, method calls and closures). Also, the DSL is statically
|
||
typed, to make it programmer-readable without any knowledge of the DSL itself.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
Remember that, inside the Groovy contract file, you have to provide the fully
|
||
qualified name to the <code>Contract</code> class and <code>make</code> static imports, such as
|
||
<code>org.springframework.cloud.spec.Contract.make { …​ }</code>. You can also provide an import to
|
||
the <code>Contract</code> class: <code>import org.springframework.cloud.spec.Contract</code> and then call
|
||
<code>Contract.make { …​ }</code>.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
Spring Cloud Contract supports defining multiple contracts in a single file.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following is a complete example of a Groovy contract definition:</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 is a complete example of a YAML contract definition:</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="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
You can compile contracts to stubs mapping using standalone maven command:
|
||
<code>mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert</code>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_limitations"><a class="link" href="#_limitations">Limitations</a></h3>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
Spring Cloud Contract Verifier does not properly support XML. Please use JSON or
|
||
help us implement this feature.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
The support for verifying the size of JSON arrays is experimental. If you want
|
||
to turn it on, please set the value of the following system property to <code>true</code>:
|
||
<code>spring.cloud.contract.verifier.assert.size</code>. By default, this feature is set to <code>false</code>.
|
||
You can also provide the <code>assertJsonSize</code> property in the plugin configuration.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
Because JSON structure can have any form, it can be impossible to parse it
|
||
properly when using the Groovy DSL and the <code>value(consumer(…​), producer(…​))</code> notation in <code>GString</code>. That
|
||
is why you should use the Groovy Map notation.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_common_top_level_elements"><a class="link" href="#_common_top_level_elements">Common Top-Level elements</a></h3>
|
||
<div class="paragraph">
|
||
<p>The following sections describe the most common top-level elements:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><a href="#contract-dsl-description">Description</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-name">Name</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-ignoring-contracts">Ignoring Contracts</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-passing-values-from-files">Passing Values from Files</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-http-top-level-elements">HTTP Top-Level Elements</a></p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-description"><a class="link" href="#contract-dsl-description">Description</a></h4>
|
||
<div class="paragraph">
|
||
<p>You can add a <code>description</code> to your contract. The description is arbitrary text. The
|
||
following code shows an example:</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="contract-dsl-name"><a class="link" href="#contract-dsl-name">Name</a></h4>
|
||
<div class="paragraph">
|
||
<p>You can provide a name for your contract. Assume that you provided the following name:
|
||
<code>should register a user</code>. If you do so, the name of the autogenerated test is
|
||
<code>validate_should_register_a_user</code>. Also, the name of the stub in a WireMock stub is
|
||
<code>should_register_a_user.json</code>.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
You must ensure that the name does not contain any characters that make the
|
||
generated test not compile. Also, remember that, if you provide the same name for
|
||
multiple contracts, your autogenerated tests fail to compile and your generated stubs
|
||
override each other.
|
||
</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>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-ignoring-contracts"><a class="link" href="#contract-dsl-ignoring-contracts">Ignoring Contracts</a></h4>
|
||
<div class="paragraph">
|
||
<p>If you want to ignore a contract, you can either set a value of ignored contracts in the
|
||
plugin configuration or set the <code>ignored</code> property on the contract itself:</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="contract-dsl-passing-values-from-files"><a class="link" href="#contract-dsl-passing-values-from-files">Passing Values from Files</a></h4>
|
||
<div class="paragraph">
|
||
<p>Starting with version <code>1.2.0</code>, you can pass values from files. Assume that you have the
|
||
following resources in our project.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">└── src
|
||
└── test
|
||
└── resources
|
||
└── contracts
|
||
├── readFromFile.groovy
|
||
├── request.json
|
||
└── response.json</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Further assume that your contract is as follows:</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 class="paragraph">
|
||
<p>Further assume that the JSON files is as follows:</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><strong>request.json</strong></p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json"></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><strong>response.json</strong></p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json"></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>When test or stub generation takes place, the contents of the file is passed to the body
|
||
of a request or a response. The name of the file needs to be a file with location
|
||
relative to the folder in which the contract lays.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you need to pass the contents of a file in a binary form
|
||
it’s enough for you to use the <code>fileAsBytes</code> method in Groovy DSL or <code>bodyFromFileAsBytes</code> field in YAML.</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 class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
You should use this approach whenever you want to work with binary payloads both for HTTP and messaging.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-http-top-level-elements"><a class="link" href="#contract-dsl-http-top-level-elements">HTTP Top-Level Elements</a></h4>
|
||
<div class="paragraph">
|
||
<p>The following methods can be called in the top-level closure of a contract definition.
|
||
<code>request</code> and <code>response</code> are mandatory. <code>priority</code> is optional.</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 class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
If you want to make your contract have a <strong>higher</strong> value of priority
|
||
you need to pass a <strong>lower</strong> number to the <code>priority</code> tag / method. E.g. <code>priority</code> with
|
||
value <code>5</code> has <strong>higher</strong> priority than <code>priority</code> with value <code>10</code>.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_request"><a class="link" href="#_request">Request</a></h3>
|
||
<div class="paragraph">
|
||
<p>The HTTP protocol requires only <strong>method and url</strong> to be specified in a request. The
|
||
same information is mandatory in request definition of the Contract.</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 class="paragraph">
|
||
<p>It is possible to specify an absolute rather than relative <code>url</code>, but using <code>urlPath</code> is
|
||
the recommended way, as doing so makes the tests <strong>host-independent</strong>.</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 class="paragraph">
|
||
<p><code>request</code> may contain <strong>query parameters</strong>.</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 class="paragraph">
|
||
<p><code>request</code> may contain additional <strong>request headers</strong>, as shown in the following example:</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 class="paragraph">
|
||
<p><code>request</code> may contain additional <strong>request cookies</strong>, as shown in the following example:</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 class="paragraph">
|
||
<p><code>request</code> may contain a <strong>request body</strong>:</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 class="paragraph">
|
||
<p><code>request</code> may contain <strong>multipart</strong> elements. To include multipart elements, use the
|
||
<code>multipart</code> method/section, as shown in the following examples</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 class="paragraph">
|
||
<p>In the preceding example, we define parameters in either of two ways:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<div class="title">Groovy DSL</div>
|
||
<ul>
|
||
<li>
|
||
<p>Directly, by using the map notation, where the value can be a dynamic property (such as
|
||
<code>formParameter: $(consumer(…​), producer(…​))</code>).</p>
|
||
</li>
|
||
<li>
|
||
<p>By using the <code>named(…​)</code> method that lets you set a named parameter. A named parameter
|
||
can set a <code>name</code> and <code>content</code>. You can call it either via a method with two arguments,
|
||
such as <code>named("fileName", "fileContent")</code>, or via a map notation, such as
|
||
<code>named(name: "fileName", content: "fileContent")</code>.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="ulist">
|
||
<div class="title">YAML</div>
|
||
<ul>
|
||
<li>
|
||
<p>The multipart parameters are set via <code>multipart.params</code> section</p>
|
||
</li>
|
||
<li>
|
||
<p>The named parameters (the <code>fileName</code> and <code>fileContent</code> for a given parameter name)
|
||
can be set via the <code>multipart.named</code> section. That section contains
|
||
the <code>paramName</code> (name of the parameter), <code>fileName</code> (name of the file),
|
||
<code>fileContent</code> (content of the file) fields</p>
|
||
</li>
|
||
<li>
|
||
<p>The dynamic bits can be set via the <code>matchers.multipart</code> section</p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>for parameters use the <code>params</code> section that can accept
|
||
<code>regex</code> or a <code>predefined</code> regular expression</p>
|
||
</li>
|
||
<li>
|
||
<p>for named params use the <code>named</code> section where first you
|
||
define the parameter name via <code>paramName</code> and then you can pass the
|
||
parametrization of either <code>fileName</code> or <code>fileContent</code> via
|
||
<code>regex</code> or a <code>predefined</code> regular expression</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>From this contract, the generated test is as follows:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Content-Type", "multipart/form-data;boundary=AaB03x")
|
||
.param("formParameter", "\"formParameterValue\"")
|
||
.param("someBooleanParameter", "true")
|
||
.multiPart("file", "filename.csv", "file content".getBytes());
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.put("/multipart");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The WireMock stub is as follows:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json"></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_response"><a class="link" href="#_response">Response</a></h3>
|
||
<div class="paragraph">
|
||
<p>The response must contain an <strong>HTTP status code</strong> and may contain other information. The
|
||
following code shows an example:</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 class="paragraph">
|
||
<p>Besides status, the response may contain <strong>headers</strong>, <strong>cookies</strong> and a <strong>body</strong>, both of which are
|
||
specified the same way as in the request (see the previous paragraph).</p>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
Via the Groovy DSL you can reference the <code>org.springframework.cloud.contract.spec.internal.HttpStatus</code>
|
||
methods to provide a meaningful status instead of a digit. E.g. you can call
|
||
<code>OK()</code> for a status <code>200</code> or <code>BAD_REQUEST()</code> for <code>400</code>.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_dynamic_properties"><a class="link" href="#_dynamic_properties">Dynamic properties</a></h3>
|
||
<div class="paragraph">
|
||
<p>The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not
|
||
want to force the consumers to stub their clocks to always return the same value of time
|
||
so that it gets matched by the stub.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For Groovy DSL you can provide the dynamic parts in your contracts
|
||
in two ways: pass them directly in the body or set them in a separate section called
|
||
<code>bodyMatchers</code>.</p>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
Before 2.0.0 these were set using <code>testMatchers</code> and <code>stubMatchers</code>,
|
||
check out the <a href="https://github.com/spring-cloud/spring-cloud-contract/wiki/Spring-Cloud-Contract-2.0-Migration-Guide">migration guide</a> for more information.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For YAML you can only use the <code>matchers</code> section.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_dynamic_properties_inside_the_body"><a class="link" href="#_dynamic_properties_inside_the_body">Dynamic properties inside the body</a></h4>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
This section is valid only for Groovy DSL. Check out the
|
||
<a href="#contract-matchers">Dynamic Properties in the Matchers Sections</a> section for YAML examples of a similar feature.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can set the properties inside the body either with the <code>value</code> method or, if you use
|
||
the Groovy map notation, with <code>$()</code>. The following example shows how to set dynamic
|
||
properties with the value method:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">value(consumer(...), producer(...))
|
||
value(c(...), p(...))
|
||
value(stub(...), test(...))
|
||
value(client(...), server(...))</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows how to set dynamic properties with <code>$()</code>:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">$(consumer(...), producer(...))
|
||
$(c(...), p(...))
|
||
$(stub(...), test(...))
|
||
$(client(...), server(...))</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Both approaches work equally well. <code>stub</code> and <code>client</code> methods are aliases over the <code>consumer</code>
|
||
method. Subsequent sections take a closer look at what you can do with those values.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_regular_expressions"><a class="link" href="#_regular_expressions">Regular expressions</a></h4>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
This section is valid only for Groovy DSL. Check out the
|
||
<a href="#contract-matchers">Dynamic Properties in the Matchers Sections</a> section for YAML examples of a similar feature.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can use regular expressions to write your requests in Contract DSL. Doing so is
|
||
particularly useful when you want to indicate that a given response should be provided
|
||
for requests that follow a given pattern. Also, you can use regular expressions when you
|
||
need to use patterns and not exact values both for your test and your server side tests.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Make sure that regex matches a whole region of a sequence as internally a call to
|
||
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#matches--">Pattern.matches()</a>
|
||
is called. For instance, <code>abc</code> pattern doesn’t match <code>aabc</code> string but <code>.abc</code> does.
|
||
There are several additional <a href="#regex-limitations">known limitations</a> as well.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows how to use regular expressions to write a request:</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>You can also provide only one side of the communication with a regular expression. If you
|
||
do so, then the contract engine automatically provides the generated string that matches
|
||
the provided regular expression. The following code shows an example:</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>In the preceding example, the opposite side of the communication has the respective data
|
||
generated for request and response.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud Contract comes with a series of predefined regular expressions that you can
|
||
use in your contracts, 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"></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In your contract, you can use it as shown in the following example:</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>To make matters even simpler you can use a set of predefined objects that will automatically assume that you want a regular expression to be passed.
|
||
All of those methods start with <code>any</code> prefix:</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>and this is an example of how you can reference those methods:</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="sect4">
|
||
<h5 id="regex-limitations"><a class="link" href="#regex-limitations">Limitations</a></h5>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
Due to certain limitations of <code>Xeger</code> library that generates string out of
|
||
regex, do not use <code>$</code> and <code>^</code> signs in your regex if you rely on automatic
|
||
generation. <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/899">Issue 899</a>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
Do not use <code>LocalDate</code> instance as a value for <code>$</code> like this <code>$(consumer(LocalDate.now()))</code>.
|
||
It causes <code>java.lang.StackOverflowError</code>. Use <code>$(consumer(LocalDate.now().toString()))</code> instead.
|
||
<a href="https://github.com/spring-cloud/spring-cloud-contract/issues/900">Issue 900</a>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_passing_optional_parameters"><a class="link" href="#_passing_optional_parameters">Passing Optional Parameters</a></h4>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
This section is valid only for Groovy DSL. Check out the
|
||
<a href="#contract-matchers">Dynamic Properties in the Matchers Sections</a> section for YAML examples of a similar feature.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>It is possible to provide optional parameters in your contract. However, you can provide
|
||
optional parameters only for the following:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><em>STUB</em> side of the Request</p>
|
||
</li>
|
||
<li>
|
||
<p><em>TEST</em> side of the Response</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows how to provide optional parameters:</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>By wrapping a part of the body with the <code>optional()</code> method, you create a regular
|
||
expression that must be present 0 or more times.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you use Spock for, the following test would be generated from the previous example:</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 stub would also be generated:</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>
|
||
<div class="sect3">
|
||
<h4 id="_executing_custom_methods_on_the_server_side"><a class="link" href="#_executing_custom_methods_on_the_server_side">Executing Custom Methods on the Server Side</a></h4>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
This section is valid only for Groovy DSL. Check out the
|
||
<a href="#contract-matchers">Dynamic Properties in the Matchers Sections</a> section for YAML examples of a similar feature.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can define a method call that executes on the server side during the test. Such a
|
||
method can be added to the class defined as "baseClassForTests" in the configuration. The
|
||
following code shows an example of the contract portion of the test case:</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 code shows the base class portion of the test case:</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="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
You cannot use both a String and <code>execute</code> to perform concatenation. For
|
||
example, calling <code>header('Authorization', 'Bearer ' + execute('authToken()'))</code> leads to
|
||
improper results. Instead, call <code>header('Authorization', execute('authToken()'))</code> and
|
||
ensure that the <code>authToken()</code> method returns everything you need.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The type of the object read from the JSON can be one of the following, depending on the
|
||
JSON path:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>String</code>: If you point to a <code>String</code> value in the JSON.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>JSONArray</code>: If you point to a <code>List</code> in the JSON.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>Map</code>: If you point to a <code>Map</code> in the JSON.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>Number</code>: If you point to <code>Integer</code>, <code>Double</code> etc. in the JSON.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>Boolean</code>: If you point to a <code>Boolean</code> in the JSON.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In the request part of the contract, you can specify that the <code>body</code> should be taken from
|
||
a method.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
You must provide both the consumer and the producer side. The <code>execute</code> part
|
||
is applied for the whole body - not for parts of it.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows how to read an object from JSON:</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 preceding example results in calling the <code>hashCode()</code> method in the request body.
|
||
It should resemble the following code:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.body(hashCode());
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.get("/something");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_referencing_the_request_from_the_response"><a class="link" href="#_referencing_the_request_from_the_response">Referencing the Request from the Response</a></h4>
|
||
<div class="paragraph">
|
||
<p>The best situation is to provide fixed values, but sometimes you need to reference a
|
||
request in your response.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you’re writing contracts using Groovy DSL, you can use the <code>fromRequest()</code> method, which lets
|
||
you reference a bunch of elements from the HTTP request. You can use the following
|
||
options:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>fromRequest().url()</code>: Returns the request URL and query parameters.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().query(String key)</code>: Returns the first query parameter with a given name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().query(String key, int index)</code>: Returns the nth query parameter with a
|
||
given name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().path()</code>: Returns the full path.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().path(int index)</code>: Returns the nth path element.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().header(String key)</code>: Returns the first header with a given name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().header(String key, int index)</code>: Returns the nth header with a given name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().body()</code>: Returns the full request body.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>fromRequest().body(String jsonPath)</code>: Returns the element from the request that
|
||
matches the JSON Path.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you’re using the YAML contract definition you have to use the
|
||
<a href="https://handlebarsjs.com/">Handlebars</a> <code>{{{ }}}</code> notation with custom, Spring Cloud Contract
|
||
functions to achieve this.</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>{{{ request.url }}}</code>: Returns the request URL and query parameters.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ request.query.key.[index] }}}</code>: Returns the nth query parameter with a given name.
|
||
E.g. for key <code>foo</code>, first entry <code>{{{ request.query.foo.[0] }}}</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ request.path }}}</code>: Returns the full path.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ request.path.[index] }}}</code>: Returns the nth path element. E.g.
|
||
for first entry <code>`</code>{{{ request.path.[0] }}}</p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ request.headers.key }}}</code>: Returns the first header with a given name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ request.headers.key.[index] }}}</code>: Returns the nth header with a given name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ request.body }}}</code>: Returns the full request body.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>{{{ jsonpath this 'your.json.path' }}}</code>: Returns the element from the request that
|
||
matches the JSON Path. E.g. for json path <code>$.foo</code> - <code>{{{ jsonpath this '$.foo' }}}</code></p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Consider the following contract:</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 class="paragraph">
|
||
<p>Running a JUnit test generation leads to a test that resembles the following example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Authorization", "secret")
|
||
.header("Authorization", "secret2")
|
||
.body("{\"foo\":\"bar\",\"baz\":5}");
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.queryParam("foo","bar")
|
||
.queryParam("foo","bar2")
|
||
.get("/api/v1/xxxx");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
assertThat(response.header("Authorization")).isEqualTo("foo secret bar");
|
||
// and:
|
||
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
||
assertThatJson(parsedJson).field("['fullBody']").isEqualTo("{\"foo\":\"bar\",\"baz\":5}");
|
||
assertThatJson(parsedJson).field("['authorization']").isEqualTo("secret");
|
||
assertThatJson(parsedJson).field("['authorization2']").isEqualTo("secret2");
|
||
assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx");
|
||
assertThatJson(parsedJson).field("['param']").isEqualTo("bar");
|
||
assertThatJson(parsedJson).field("['paramIndex']").isEqualTo("bar2");
|
||
assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1");
|
||
assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5);
|
||
assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar");
|
||
assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2");
|
||
assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla");</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>As you can see, elements from the request have been properly referenced in the response.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The generated WireMock stub should resemble the following example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
|
||
"request" : {
|
||
"urlPath" : "/api/v1/xxxx",
|
||
"method" : "POST",
|
||
"headers" : {
|
||
"Authorization" : {
|
||
"equalTo" : "secret2"
|
||
}
|
||
},
|
||
"queryParameters" : {
|
||
"foo" : {
|
||
"equalTo" : "bar2"
|
||
}
|
||
},
|
||
"bodyPatterns" : [ {
|
||
"matchesJsonPath" : "$[?(@.['baz'] == 5)]"
|
||
}, {
|
||
"matchesJsonPath" : "$[?(@.['foo'] == 'bar')]"
|
||
} ]
|
||
},
|
||
"response" : {
|
||
"status" : 200,
|
||
"body" : "{\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"path\":\"{{{request.path}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"param\":\"{{{request.query.foo.[0]}}}\",\"pathIndex\":\"{{{request.path.[1]}}}\",\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"url\":\"{{{request.url}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\"}",
|
||
"headers" : {
|
||
"Authorization" : "{{{request.headers.Authorization.[0]}}};foo"
|
||
},
|
||
"transformers" : [ "response-template" ]
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Sending a request such as the one presented in the <code>request</code> part of the contract results
|
||
in sending the following response body:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
|
||
"url" : "/api/v1/xxxx?foo=bar&foo=bar2",
|
||
"path" : "/api/v1/xxxx",
|
||
"pathIndex" : "v1",
|
||
"param" : "bar",
|
||
"paramIndex" : "bar2",
|
||
"authorization" : "secret",
|
||
"authorization2" : "secret2",
|
||
"fullBody" : "{\"foo\":\"bar\",\"baz\":5}",
|
||
"responseFoo" : "bar",
|
||
"responseBaz" : 5,
|
||
"responseBaz2" : "Bla bla bar bla bla"
|
||
}</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 feature works only with WireMock having a version greater than or equal
|
||
to 2.5.1. The Spring Cloud Contract Verifier uses WireMock’s
|
||
<code>response-template</code> response transformer. It uses Handlebars to convert the Mustache <code>{{{ }}}</code> templates into
|
||
proper values. Additionally, it registers two helper functions:
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>escapejsonbody</code>: Escapes the request body in a format that can be embedded in a JSON.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>jsonpath</code>: For a given parameter, find an object in the request body.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_registering_your_own_wiremock_extension"><a class="link" href="#_registering_your_own_wiremock_extension">Registering Your Own WireMock Extension</a></h4>
|
||
<div class="paragraph">
|
||
<p>WireMock lets you register custom extensions. By default, Spring Cloud Contract registers
|
||
the transformer, which lets you reference a request from a response. If you want to
|
||
provide your own extensions, you can register an implementation of the
|
||
<code>org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions</code> interface.
|
||
Since we use the spring.factories extension approach, you can create an entry in
|
||
<code>META-INF/spring.factories</code> file similar to the following:</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 is an example of a custom extension:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="title">TestWireMockExtensions.groovy</div>
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"></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">
|
||
Remember to override the <code>applyGlobally()</code> method and set it to <code>false</code> if you
|
||
want the transformation to be applied only for a mapping that explicitly requires it.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="contract-matchers"><a class="link" href="#contract-matchers">Dynamic Properties in the Matchers Sections</a></h4>
|
||
<div class="paragraph">
|
||
<p>If you work with <a href="https://docs.pact.io/">Pact</a>, the following discussion may seem familiar.
|
||
Quite a few users are used to having a separation between the body and setting the
|
||
dynamic parts of a contract.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can use the <code>bodyMatchers</code> section for two reasons:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>Define the dynamic values that should end up in a stub.
|
||
You can set it in the <code>request</code> or <code>inputMessage</code> part of your contract.</p>
|
||
</li>
|
||
<li>
|
||
<p>Verify the result of your test.
|
||
This section is present in the <code>response</code> or <code>outputMessage</code> side of the
|
||
contract.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the
|
||
following matching possibilities:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<div class="title">Groovy DSL</div>
|
||
<ul>
|
||
<li>
|
||
<p>For the stubs(in tests on the Consumer’s side):</p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>byEquality()</code>: The value taken from the consumer’s request via the provided JSON Path must be
|
||
equal to the value provided in the contract.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byRegex(…​)</code>: The value taken from the consumer’s request via the provided JSON Path must
|
||
match the regex. You can also pass the type of the expected matched value (e.g. <code>asString()</code>, <code>asLong()</code> etc.)</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byDate()</code>: The value taken from the consumer’s request via the provided JSON Path must
|
||
match the regex for an ISO Date value.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byTimestamp()</code>: The value taken from the consumer’s request via the provided JSON Path must
|
||
match the regex for an ISO DateTime value.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byTime()</code>: The value taken from the consumer’s request via the provided JSON Path must
|
||
match the regex for an ISO Time value.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p>For the verification(in generated tests on the Producer’s side):</p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>byEquality()</code>: The value taken from the producer’s response via the provided JSON Path must be
|
||
equal to the provided value in the contract.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byRegex(…​)</code>: The value taken from the producer’s response via the provided JSON Path must
|
||
match the regex.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byDate()</code>: The value taken from the producer’s response via the provided JSON Path must match
|
||
the regex for an ISO Date value.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byTimestamp()</code>: The value taken from the producer’s response via the provided JSON Path must
|
||
match the regex for an ISO DateTime value.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byTime()</code>: The value taken from the producer’s response via the provided JSON Path must match
|
||
the regex for an ISO Time value.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byType()</code>: The value taken from the producer’s response via the provided JSON Path needs to be
|
||
of the same type as the type defined in the body of the response in the contract.
|
||
<code>byType</code> can take a closure, in which you can set <code>minOccurrence</code> and <code>maxOccurrence</code>. For the request side, you should use the closure to assert size of the collection.
|
||
That way, you can assert the size of the flattened collection. To check the size of an
|
||
unflattened collection, use a custom method with the <code>byCommand(…​)</code> testMatcher.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>byCommand(…​)</code>: The value taken from the producer’s response via the provided JSON Path is
|
||
passed as an input to the custom method that you provide. For example,
|
||
<code>byCommand('foo($it)')</code> results in calling a <code>foo</code> method to which the value matching the
|
||
JSON Path gets passed. The type of the object read from the JSON can be one of the
|
||
following, depending on the JSON path:</p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>String</code>: If you point to a <code>String</code> value.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>JSONArray</code>: If you point to a <code>List</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>Map</code>: If you point to a <code>Map</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>Number</code>: If you point to <code>Integer</code>, <code>Double</code>, or other kind of number.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>Boolean</code>: If you point to a <code>Boolean</code>.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p><code>byNull()</code>: The value taken from the response via the provided JSON Path must be null</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<div class="title">YAML</div>
|
||
<p><em>Please read the Groovy section for detailed explanation of
|
||
what the types mean</em></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For YAML the structure of a matcher looks like this</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-yml hljs" data-lang="yml">- path: $.foo
|
||
type: by_regex
|
||
value: bar
|
||
regexType: as_string</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Or if you want to use one of the predefined regular expressions
|
||
<code>[only_alpha_unicode, number, any_boolean, ip_address, hostname,
|
||
email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]</code>:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-yml hljs" data-lang="yml">- path: $.foo
|
||
type: by_regex
|
||
predefined: only_alpha_unicode</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Below you can find the allowed list of `type`s.</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>For <code>stubMatchers</code>:</p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>by_equality</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_regex</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_date</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_timestamp</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_time</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_type</code></p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>there are 2 additional fields accepted: <code>minOccurrence</code> and <code>maxOccurrence</code>.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p>For <code>testMatchers</code>:</p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>by_equality</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_regex</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_date</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_timestamp</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_time</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_type</code></p>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>there are 2 additional fields accepted: <code>minOccurrence</code> and <code>maxOccurrence</code>.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p><code>by_command</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>by_null</code></p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can also define which type the regular expression corresponds to via the <code>regexType</code> field. Below you can find the allowed list of regular expression types:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>as_integer</p>
|
||
</li>
|
||
<li>
|
||
<p>as_double</p>
|
||
</li>
|
||
<li>
|
||
<p>as_float,</p>
|
||
</li>
|
||
<li>
|
||
<p>as_long</p>
|
||
</li>
|
||
<li>
|
||
<p>as_short</p>
|
||
</li>
|
||
<li>
|
||
<p>as_boolean</p>
|
||
</li>
|
||
<li>
|
||
<p>as_string</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Consider the following example:</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 class="paragraph">
|
||
<p>In the preceding example, you can see the dynamic portions of the contract in the
|
||
<code>matchers</code> sections. For the request part, you can see that, for all fields but
|
||
<code>valueWithoutAMatcher</code>, the values of the regular expressions that the stub should
|
||
contain are explicitly set. For the <code>valueWithoutAMatcher</code>, the verification takes place
|
||
in the same way as without the use of matchers. In that case, the test performs an
|
||
equality check.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For the response side in the <code>bodyMatchers</code> section, we define the dynamic parts in a
|
||
similar manner. The only difference is that the <code>byType</code> matchers are also present. The
|
||
verifier engine checks four fields to verify whether the response from the test
|
||
has a value for which the JSON path matches the given field, is of the same type as the one
|
||
defined in the response body, and passes the following check (based on the method being called):</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>For <code>$.valueWithTypeMatch</code>, the engine checks whether the type is the same.</p>
|
||
</li>
|
||
<li>
|
||
<p>For <code>$.valueWithMin</code>, the engine check the type and asserts whether the size is greater
|
||
than or equal to the minimum occurrence.</p>
|
||
</li>
|
||
<li>
|
||
<p>For <code>$.valueWithMax</code>, the engine checks the type and asserts whether the size is
|
||
smaller than or equal to the maximum occurrence.</p>
|
||
</li>
|
||
<li>
|
||
<p>For <code>$.valueWithMinMax</code>, the engine checks the type and asserts whether the size is
|
||
between the min and maximum occurrence.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The resulting test would resemble the following example (note that an <code>and</code> section
|
||
separates the autogenerated assertions and the assertion from matchers):</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Content-Type", "application/json")
|
||
.body("{\"duck\":123,\"alpha\":\"abc\",\"number\":123,\"aBoolean\":true,\"date\":\"2017-01-01\",\"dateTime\":\"2017-01-01T01:23:45\",\"time\":\"01:02:34\",\"valueWithoutAMatcher\":\"foo\",\"valueWithTypeMatch\":\"string\",\"key\":{\"complex.key\":\"foo\"}}");
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.get("/get");
|
||
|
||
// 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("['valueWithoutAMatcher']").isEqualTo("foo");
|
||
// and:
|
||
assertThat(parsedJson.read("$.duck", String.class)).matches("[0-9]{3}");
|
||
assertThat(parsedJson.read("$.duck", Integer.class)).isEqualTo(123);
|
||
assertThat(parsedJson.read("$.alpha", String.class)).matches("[\\p{L}]*");
|
||
assertThat(parsedJson.read("$.alpha", String.class)).isEqualTo("abc");
|
||
assertThat(parsedJson.read("$.number", String.class)).matches("-?(\\d*\\.\\d+|\\d+)");
|
||
assertThat(parsedJson.read("$.aBoolean", String.class)).matches("(true|false)");
|
||
assertThat(parsedJson.read("$.date", String.class)).matches("(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])");
|
||
assertThat(parsedJson.read("$.dateTime", String.class)).matches("([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
|
||
assertThat(parsedJson.read("$.time", String.class)).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
|
||
assertThat((Object) parsedJson.read("$.valueWithTypeMatch")).isInstanceOf(java.lang.String.class);
|
||
assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMin", java.util.Collection.class)).as("$.valueWithMin").hasSizeGreaterThanOrEqualTo(1);
|
||
assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMax", java.util.Collection.class)).as("$.valueWithMax").hasSizeLessThanOrEqualTo(3);
|
||
assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinMax", java.util.Collection.class)).as("$.valueWithMinMax").hasSizeBetween(1, 3);
|
||
assertThat((Object) parsedJson.read("$.valueWithMinEmpty")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).as("$.valueWithMinEmpty").hasSizeGreaterThanOrEqualTo(0);
|
||
assertThat((Object) parsedJson.read("$.valueWithMaxEmpty")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).as("$.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0);
|
||
assertThatValueIsANumber(parsedJson.read("$.duck"));
|
||
assertThat(parsedJson.read("$.['key'].['complex.key']", String.class)).isEqualTo("foo");</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">
|
||
Notice that, for the <code>byCommand</code> method, the example calls the
|
||
<code>assertThatValueIsANumber</code>. This method must be defined in the test base class or be
|
||
statically imported to your tests. Notice that the <code>byCommand</code> call was converted to
|
||
<code>assertThatValueIsANumber(parsedJson.read("$.duck"));</code>. That means that the engine took
|
||
the method name and passed the proper JSON path as a parameter to it.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The resulting WireMock stub is in the following example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json"></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">
|
||
If you use a <code>matcher</code>, then the part of the request and response that the
|
||
<code>matcher</code> addresses with the JSON Path gets removed from the assertion. In the case of
|
||
verifying a collection, you must create matchers for <strong>all</strong> the elements of the
|
||
collection.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Consider the following example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">Contract.make {
|
||
request {
|
||
method 'GET'
|
||
url("/foo")
|
||
}
|
||
response {
|
||
status OK()
|
||
body(events: [[
|
||
operation : 'EXPORT',
|
||
eventId : '16f1ed75-0bcc-4f0d-a04d-3121798faf99',
|
||
status : 'OK'
|
||
], [
|
||
operation : 'INPUT_PROCESSING',
|
||
eventId : '3bb4ac82-6652-462f-b6d1-75e424a0024a',
|
||
status : 'OK'
|
||
]
|
||
]
|
||
)
|
||
bodyMatchers {
|
||
jsonPath('$.events[0].operation', byRegex('.+'))
|
||
jsonPath('$.events[0].eventId', byRegex('^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'))
|
||
jsonPath('$.events[0].status', byRegex('.+'))
|
||
}
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The preceding code leads to creating the following test (the code block shows only the assertion section):</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">and:
|
||
DocumentContext parsedJson = JsonPath.parse(response.body.asString())
|
||
assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("16f1ed75-0bcc-4f0d-a04d-3121798faf99")
|
||
assertThatJson(parsedJson).array("['events']").contains("['operation']").isEqualTo("EXPORT")
|
||
assertThatJson(parsedJson).array("['events']").contains("['operation']").isEqualTo("INPUT_PROCESSING")
|
||
assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("3bb4ac82-6652-462f-b6d1-75e424a0024a")
|
||
assertThatJson(parsedJson).array("['events']").contains("['status']").isEqualTo("OK")
|
||
and:
|
||
assertThat(parsedJson.read("\$.events[0].operation", String.class)).matches(".+")
|
||
assertThat(parsedJson.read("\$.events[0].eventId", String.class)).matches("^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})\$")
|
||
assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+")</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>As you can see, the assertion is malformed. Only the first element of the array got
|
||
asserted. In order to fix this, you should apply the assertion to the whole <code>$.events</code>
|
||
collection and assert it with the <code>byCommand(…​)</code> method.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_jax_rs_support"><a class="link" href="#_jax_rs_support">JAX-RS Support</a></h3>
|
||
<div class="paragraph">
|
||
<p>The Spring Cloud Contract Verifier supports the JAX-RS 2 Client API. The base class needs
|
||
to define <code>protected WebTarget webTarget</code> and server initialization. The only option for
|
||
testing JAX-RS API is to start a web server. Also, a request with a body needs to have a
|
||
content type set. Otherwise, the default of <code>application/octet-stream</code> gets used.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In order to use JAX-RS mode, use the following settings:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">testMode == 'JAXRSCLIENT'</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows a generated test API:</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>
|
||
<div class="sect2">
|
||
<h3 id="_async_support"><a class="link" href="#_async_support">Async Support</a></h3>
|
||
<div class="paragraph">
|
||
<p>If you’re using asynchronous communication on the server side (your controllers are
|
||
returning <code>Callable</code>, <code>DeferredResult</code>, and so on), then, inside your contract, you must
|
||
provide an <code>async()</code> method in the <code>response</code> section. The following code shows an example:</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">org.springframework.cloud.contract.spec.Contract.make {
|
||
request {
|
||
method GET()
|
||
url '/get'
|
||
}
|
||
response {
|
||
status OK()
|
||
body 'Passed'
|
||
async()
|
||
}
|
||
}</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">response:
|
||
async: true</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can also use the <code>fixedDelayMilliseconds</code> method / property to add delay to your stubs.</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">org.springframework.cloud.contract.spec.Contract.make {
|
||
request {
|
||
method GET()
|
||
url '/get'
|
||
}
|
||
response {
|
||
status 200
|
||
body 'Passed'
|
||
fixedDelayMilliseconds 1000
|
||
}
|
||
}</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">response:
|
||
fixedDelayMilliseconds: 1000</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_working_with_context_paths"><a class="link" href="#_working_with_context_paths">Working with Context Paths</a></h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud Contract supports context paths.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
The only change needed to fully support context paths is the switch on the
|
||
<strong>PRODUCER</strong> side. Also, the autogenerated tests must use <strong>EXPLICIT</strong> mode. The consumer
|
||
side remains untouched. In order for the generated test to pass, you must use <strong>EXPLICIT</strong>
|
||
mode.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</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"><plugin>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||
<version>${spring-cloud-contract.version}</version>
|
||
<extensions>true</extensions>
|
||
<configuration>
|
||
<testMode>EXPLICIT</testMode>
|
||
</configuration>
|
||
</plugin></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">contracts {
|
||
testMode = 'EXPLICIT'
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>That way, you generate a test that <strong>DOES NOT</strong> use MockMvc. It means that you generate
|
||
real requests and you need to setup your generated test’s base class to work on a real
|
||
socket.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Consider the following contract:</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 how to set up a base class and Rest Assured:</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>If you do it this way:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>All of your requests in the autogenerated tests are sent to the real endpoint with your
|
||
context path included (for example, <code>/my-context-path/url</code>).</p>
|
||
</li>
|
||
<li>
|
||
<p>Your contracts reflect that you have a context path. Your generated stubs also have
|
||
that information (for example, in the stubs, you have to call <code>/my-context-path/url</code>).</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_working_with_webflux"><a class="link" href="#_working_with_webflux">Working with WebFlux</a></h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud Contract offers two ways of working with WebFlux.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_webflux_with_webtestclient"><a class="link" href="#_webflux_with_webtestclient">WebFlux with WebTestClient</a></h4>
|
||
<div class="paragraph">
|
||
<p>One of them is via the <code>WebTestClient</code> mode.</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"><plugin>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||
<version>${spring-cloud-contract.version}</version>
|
||
<extensions>true</extensions>
|
||
<configuration>
|
||
<testMode>WEBTESTCLIENT</testMode>
|
||
</configuration>
|
||
</plugin></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">contracts {
|
||
testMode = 'WEBTESTCLIENT'
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows how to set up a <code>WebTestClient</code> base class and <code>RestAssured</code>
|
||
for WebFlux:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">import io.restassured.module.webtestclient.RestAssuredWebTestClient;
|
||
import org.junit.Before;
|
||
|
||
public abstract class BeerRestBase {
|
||
|
||
@Before
|
||
public void setup() {
|
||
RestAssuredWebTestClient.standaloneSetup(
|
||
new ProducerController(personToCheck -> personToCheck.age >= 20));
|
||
}
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_webflux_with_explicit_mode"><a class="link" href="#_webflux_with_explicit_mode">WebFlux with Explicit mode</a></h4>
|
||
<div class="paragraph">
|
||
<p>Another way is with the <code>EXPLICIT</code> mode in your generated tests
|
||
to work with WebFlux.</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"><plugin>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||
<version>${spring-cloud-contract.version}</version>
|
||
<extensions>true</extensions>
|
||
<configuration>
|
||
<testMode>EXPLICIT</testMode>
|
||
</configuration>
|
||
</plugin></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">contracts {
|
||
testMode = 'EXPLICIT'
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows how to set up a base class and Rest Assured for Web Flux:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy"> // your tests go here
|
||
|
||
// in this config class you define all controllers and mocked services
|
||
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_xml_support_for_rest"><a class="link" href="#_xml_support_for_rest">XML Support for REST</a></h3>
|
||
<div class="paragraph">
|
||
<p>For REST contracts, we also support XML request and response body.
|
||
The XML body has to be passed within the <code>body</code> element
|
||
as a <code>String</code> or <code>GString</code>. Also body matchers can be provided for
|
||
both request and response. In place of the <code>jsonPath(…​)</code> method, the <code>org.springframework.cloud.contract.spec.internal.BodyMatchers.xPath</code>
|
||
method should be used, with the desired <code>xPath</code> provided as the first argument
|
||
and the appropriate <code>MatchingType</code> as second. All the body matchers apart from <code>byType()</code> are supported.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here is an example of a Groovy DSL contract with XML response body:</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>And below is an example of a YAML contract with XML request and response bodies:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">include::{verifier_core_path}/src/test/resources/yml/contract_rest_xml.yml</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here is an example of an automatically generated test for XML response body:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Test
|
||
public void validate_xmlMatches() throws Exception {
|
||
// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Content-Type", "application/xml");
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request).get("/get");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
// and:
|
||
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
|
||
.newDocumentBuilder();
|
||
Document parsedXml = documentBuilder.parse(new InputSource(
|
||
new StringReader(response.getBody().asString())));
|
||
// and:
|
||
assertThat(valueFromXPath(parsedXml, "/test/list/elem/text()")).isEqualTo("abc");
|
||
assertThat(valueFromXPath(parsedXml,"/test/list/elem[2]/text()")).isEqualTo("def");
|
||
assertThat(valueFromXPath(parsedXml, "/test/duck/text()")).matches("[0-9]{3}");
|
||
assertThat(nodeFromXPath(parsedXml, "/test/duck/xxx")).isNull();
|
||
assertThat(valueFromXPath(parsedXml, "/test/alpha/text()")).matches("[\\p{L}]*");
|
||
assertThat(valueFromXPath(parsedXml, "/test/*/complex/text()")).isEqualTo("foo");
|
||
assertThat(valueFromXPath(parsedXml, "/test/duck/@type")).isEqualTo("xtype");
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_messaging_top_level_elements"><a class="link" href="#_messaging_top_level_elements">Messaging Top-Level Elements</a></h3>
|
||
<div class="paragraph">
|
||
<p>The DSL for messaging looks a little bit different than the one that focuses on HTTP. The
|
||
following sections explain the differences:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><a href="#contract-dsl-output-triggered-method">Output Triggered by a Method</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-output-triggered-message">Output Triggered by a Message</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-consumer-producer">Consumer/Producer</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="#contract-dsl-common">Common</a></p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-output-triggered-method"><a class="link" href="#contract-dsl-output-triggered-method">Output Triggered by a Method</a></h4>
|
||
<div class="paragraph">
|
||
<p>The output message can be triggered by calling a method (such as a <code>Scheduler</code> when a was
|
||
started and a message was sent), as shown in the following example:</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 class="paragraph">
|
||
<p>In the previous example case, the output message is sent to <code>output</code> if a method called
|
||
<code>bookReturnedTriggered</code> is executed. On the message <strong>publisher’s</strong> side, we generate a
|
||
test that calls that method to trigger the message. On the <strong>consumer</strong> side, you can use
|
||
the <code>some_label</code> to trigger the message.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-output-triggered-message"><a class="link" href="#contract-dsl-output-triggered-message">Output Triggered by a Message</a></h4>
|
||
<div class="paragraph">
|
||
<p>The output message can be triggered by receiving a message, as shown in the following
|
||
example:</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 class="paragraph">
|
||
<p>In the preceding example, the output message is sent to <code>output</code> if a proper message is
|
||
received on the <code>input</code> destination. On the message <strong>publisher’s</strong> side, the engine
|
||
generates a test that sends the input message to the defined destination. On the
|
||
<strong>consumer</strong> side, you can either send a message to the input destination or use a label
|
||
(<code>some_label</code> in the example) to trigger the message.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-consumer-producer"><a class="link" href="#contract-dsl-consumer-producer">Consumer/Producer</a></h4>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
This section is valid only for Groovy DSL.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In HTTP, you have a notion of <code>client</code>/<code>stub and `server</code>/<code>test</code> notation. You can also
|
||
use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also
|
||
provides the <code>consumer</code> and <code>producer</code> methods, as presented in the following example
|
||
(note that you can use either <code>$</code> or <code>value</code> methods to provide <code>consumer</code> and <code>producer</code>
|
||
parts):</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>
|
||
<div class="sect3">
|
||
<h4 id="contract-dsl-common"><a class="link" href="#contract-dsl-common">Common</a></h4>
|
||
<div class="paragraph">
|
||
<p>In the <code>input</code> or <code>outputMessage</code> section you can call <code>assertThat</code> with the name
|
||
of a <code>method</code> (e.g. <code>assertThatMessageIsOnTheQueue()</code>) that you have defined in the
|
||
base class or in a static import. Spring Cloud Contract will execute that method
|
||
in the generated test.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_multiple_contracts_in_one_file"><a class="link" href="#_multiple_contracts_in_one_file">Multiple Contracts in One File</a></h3>
|
||
<div class="paragraph">
|
||
<p>You can define multiple contracts in one file. Such a contract might resemble the
|
||
following example:</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 class="paragraph">
|
||
<p>In the preceding example, one contract has the <code>name</code> field and the other does not. This
|
||
leads to generation of two tests that look 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">package org.springframework.cloud.contract.verifier.tests.com.hello;
|
||
|
||
import com.example.TestBase;
|
||
import com.jayway.jsonpath.DocumentContext;
|
||
import com.jayway.jsonpath.JsonPath;
|
||
import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
|
||
import com.jayway.restassured.response.ResponseOptions;
|
||
import org.junit.Test;
|
||
|
||
import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;
|
||
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
|
||
import static org.assertj.core.api.Assertions.assertThat;
|
||
|
||
public class V1Test extends TestBase {
|
||
|
||
@Test
|
||
public void validate_should_post_a_user() throws Exception {
|
||
// given:
|
||
MockMvcRequestSpecification request = given();
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.post("/users/1");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
}
|
||
|
||
@Test
|
||
public void validate_withList_1() throws Exception {
|
||
// given:
|
||
MockMvcRequestSpecification request = given();
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.post("/users/2");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
}
|
||
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Notice that, for the contract that has the <code>name</code> field, the generated test method is named
|
||
<code>validate_should_post_a_user</code>. For the one that does not have the name, it is called
|
||
<code>validate_withList_1</code>. It corresponds to the name of the file <code>WithList.groovy</code> and the
|
||
index of the contract in the list.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The generated stubs is shown in the following example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code>should post a user.json
|
||
1_WithList.json</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>As you can see, the first file got the <code>name</code> parameter from the contract. The second
|
||
got the name of the contract file (<code>WithList.groovy</code>) prefixed with the index (in this
|
||
case, the contract had an index of <code>1</code> in the list of contracts in the file).</p>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
As you can see, it is much better if you name your contracts because doing so makes
|
||
your tests far more meaningful.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_generating_spring_rest_docs_snippets_from_the_contracts"><a class="link" href="#_generating_spring_rest_docs_snippets_from_the_contracts">Generating Spring REST Docs snippets from the contracts</a></h3>
|
||
<div class="paragraph">
|
||
<p>When you want to include the requests and responses of your API using Spring REST Docs,
|
||
you only need to make some minor changes to your setup if you are using MockMvc and RestAssuredMockMvc.
|
||
Simply include the following dependencies if you haven’t already.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<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">
|
||
<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 class="paragraph">
|
||
<p>Next you need to make some changes to your base class like the following example.</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>In case you are using the standalone setup, you can set up RestAssuredMockMvc like this:</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="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
You don’t need to specify the output directory for the generated snippets since version 1.2.0.RELEASE of Spring REST Docs.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_customization"><a class="link" href="#_customization">Customization</a></h2>
|
||
<div class="sectionbody">
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
This section is valid only for Groovy DSL
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in
|
||
the remainder of this section.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_extending_the_dsl"><a class="link" href="#_extending_the_dsl">Extending the DSL</a></h3>
|
||
<div class="paragraph">
|
||
<p>You can provide your own functions to the DSL. The key requirement for this feature is to
|
||
maintain the static compatibility. Later in this document, you can see examples of:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>Creating a JAR with reusable classes.</p>
|
||
</li>
|
||
<li>
|
||
<p>Referencing of these classes in the DSLs.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can find the full example
|
||
<a href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples">here</a>.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_common_jar"><a class="link" href="#_common_jar">Common JAR</a></h4>
|
||
<div class="paragraph">
|
||
<p>The following examples show three classes that can be reused in the DSLs.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><strong>PatternUtils</strong> contains functions used by both the <strong>consumer</strong> and the <strong>producer</strong>.</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>ConsumerUtils</strong> contains functions used by the <strong>consumer</strong>.</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>ProducerUtils</strong> contains functions used by the <strong>producer</strong>.</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>
|
||
<div class="sect3">
|
||
<h4 id="_adding_the_dependency_to_the_project"><a class="link" href="#_adding_the_dependency_to_the_project">Adding the Dependency to the Project</a></h4>
|
||
<div class="paragraph">
|
||
<p>In order for the plugins and IDE to be able to reference the common JAR classes, you need
|
||
to pass the dependency to your project.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_test_the_dependency_in_the_projects_dependencies"><a class="link" href="#_test_the_dependency_in_the_projects_dependencies">Test the Dependency in the Project’s Dependencies</a></h4>
|
||
<div class="paragraph">
|
||
<p>First, add the common jar dependency as a test dependency. Because your contracts files
|
||
are available on the test resources path, the common jar classes automatically become
|
||
visible in your Groovy files. The following examples show how to test the dependency:</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="_test_a_dependency_in_the_plugins_dependencies"><a class="link" href="#_test_a_dependency_in_the_plugins_dependencies">Test a Dependency in the Plugin’s Dependencies</a></h4>
|
||
<div class="paragraph">
|
||
<p>Now, you must add the dependency for the plugin to reuse at runtime, as shown in the
|
||
following example:</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="_referencing_classes_in_dsls"><a class="link" href="#_referencing_classes_in_dsls">Referencing classes in DSLs</a></h4>
|
||
<div class="paragraph">
|
||
<p>You can now reference your classes in your DSL, as shown in the following example:</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="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
You can set the Spring Cloud Contract plugin up by setting <code>convertToYaml</code> to <code>true</code>. That way you will NOT have to add the dependency with the extended functionality to the consumer side, since the consumer side will be using YAML contracts instead of Groovy ones.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_using_the_pluggable_architecture"><a class="link" href="#_using_the_pluggable_architecture">Using the Pluggable Architecture</a></h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>You may encounter cases where you have your contracts have been defined in other formats,
|
||
such as YAML, RAML or PACT. In those cases, you still want to benefit from the automatic
|
||
generation of tests and stubs. You can add your own implementation for generating both
|
||
tests and stubs. Also, you can customize the way tests are generated (for example, you
|
||
can generate tests for other languages) and the way stubs are generated (for example, you
|
||
can generate stubs for other HTTP server implementations).</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_custom_contract_converter"><a class="link" href="#_custom_contract_converter">Custom Contract Converter</a></h3>
|
||
<div class="paragraph">
|
||
<p>The <code>ContractConverter</code> interface lets you register your own implementation of a contract
|
||
structure converter. The following code listing shows the <code>ContractConverter</code> interface:</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>Your implementation must define the condition on which it should start the
|
||
conversion. Also, you must define how to perform that conversion in both directions.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
Once you create your implementation, you must create a
|
||
<code>/META-INF/spring.factories</code> file in which you provide the fully qualified name of your
|
||
implementation.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The following example shows a typical <code>spring.factories</code> file:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code>org.springframework.cloud.contract.spec.ContractConverter=\
|
||
org.springframework.cloud.contract.verifier.converter.YamlContractConverter</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="pact-converter"><a class="link" href="#pact-converter">Pact Converter</a></h4>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud Contract includes support for <a href="https://docs.pact.io/">Pact</a> representation of
|
||
contracts up until v4. Instead of using the Groovy DSL, you can use Pact files. In this section, we
|
||
present how to add Pact support for your project. Note however that not all functionality is supported.
|
||
Starting with v3 you can combine multiple matcher for the same element;
|
||
you can use matchers for the body, headers, request and path; and you can use value generators.
|
||
Spring Cloud Contract currently only supports multiple matchers that are combined using the AND rule logic.
|
||
Next to that the request and path matchers are skipped during the conversion.
|
||
When using a date, time or datetime value generator with a given format,
|
||
the given format will be skipped and the ISO format will be used.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In order to properly support the Spring Cloud Contract way of doing messaging
|
||
with Pact you’ll have to provide some additional meta data entries. Below you can find a list of such entries:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>to define the destination to which a message gets sent, you have to
|
||
set a <code>metaData</code> entry in the Pact file, with key <code>sentTo</code> equal to the destination to which a message is to be sent. E.g. <code>"metaData": { "sentTo": "activemq:output" }</code></p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_pact_contract"><a class="link" href="#_pact_contract">Pact Contract</a></h4>
|
||
<div class="paragraph">
|
||
<p>Consider following example of a Pact contract, which is a file under the
|
||
<code>src/test/resources/contracts</code> folder.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript"></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The remainder of this section about using Pact refers to the preceding file.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_pact_for_producers"><a class="link" href="#_pact_for_producers">Pact for Producers</a></h4>
|
||
<div class="paragraph">
|
||
<p>On the producer side, you must add two additional dependencies to your plugin
|
||
configuration. One is the Spring Cloud Contract Pact support, and the other represents
|
||
the current Pact version that you use.</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 class="paragraph">
|
||
<p>When you execute the build of your application, a test will be generated. The generated
|
||
test might be as follows:</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("{\"clientId\":\"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("['rejectionReason']").isEqualTo("Amount too high");
|
||
// and:
|
||
assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The corresponding generated stub might be as follows:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">{
|
||
"id" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
|
||
"uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
|
||
"request" : {
|
||
"url" : "/fraudcheck",
|
||
"method" : "PUT",
|
||
"headers" : {
|
||
"Content-Type" : {
|
||
"matches" : "application/vnd\\.fraud\\.v1\\+json.*"
|
||
}
|
||
},
|
||
"bodyPatterns" : [ {
|
||
"matchesJsonPath" : "$[?(@.['loanAmount'] == 99999)]"
|
||
}, {
|
||
"matchesJsonPath" : "$[?(@.clientId =~ /([0-9]{10})/)]"
|
||
} ]
|
||
},
|
||
"response" : {
|
||
"status" : 200,
|
||
"body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
|
||
"headers" : {
|
||
"Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
|
||
},
|
||
"transformers" : [ "response-template" ]
|
||
},
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_pact_for_consumers"><a class="link" href="#_pact_for_consumers">Pact for Consumers</a></h4>
|
||
<div class="paragraph">
|
||
<p>On the producer side, you must add two additional dependencies to your project
|
||
dependencies. One is the Spring Cloud Contract Pact support, and the other represents the
|
||
current Pact version that you use.</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>
|
||
<div class="sect2">
|
||
<h3 id="_using_the_custom_test_generator"><a class="link" href="#_using_the_custom_test_generator">Using the Custom Test Generator</a></h3>
|
||
<div class="paragraph">
|
||
<p>If you want to generate tests for languages other than Java or you are not happy with the
|
||
way the verifier builds Java tests, you can register your own implementation.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>SingleTestGenerator</code> interface lets you register your own implementation. The
|
||
following code listing shows the <code>SingleTestGenerator</code> interface:</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>Again, you must provide a <code>spring.factories</code> file, such as the one shown in the following
|
||
example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code>org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
|
||
com.example.MyGenerator</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_the_custom_stub_generator"><a class="link" href="#_using_the_custom_stub_generator">Using the Custom Stub Generator</a></h3>
|
||
<div class="paragraph">
|
||
<p>If you want to generate stubs for stub servers other than WireMock, you can plug in your
|
||
own implementation of the <code>StubGenerator</code> interface. The following code listing shows the
|
||
<code>StubGenerator</code> interface:</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>Again, you must provide a <code>spring.factories</code> file, such as the one shown in the following
|
||
example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The default implementation is the WireMock stub generation.</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 provide multiple stub generator implementations. For example, from a single
|
||
DSL, you can produce both WireMock stubs and Pact files.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_the_custom_stub_runner"><a class="link" href="#_using_the_custom_stub_runner">Using the Custom Stub Runner</a></h3>
|
||
<div class="paragraph">
|
||
<p>If you decide to use a custom stub generation, you also need a custom way of running
|
||
stubs with your different stub provider.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Assume that you use <a href="https://github.com/dreamhead/moco">Moco</a> to build your stubs and that
|
||
you have written a stub generator and placed your stubs in a JAR file.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In order for Stub Runner to know how to run your stubs, you have to define a custom
|
||
HTTP Stub server implementation, which might resemble the following example:</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>Then, you can register it in your <code>spring.factories</code> file, as shown in the following
|
||
example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code>org.springframework.cloud.contract.stubrunner.HttpServerStub=\
|
||
org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now you can run stubs with Moco.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
If you do not provide any implementation, then the default (WireMock)
|
||
implementation is used. If you provide more than one, the first one on the list is used.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_the_custom_stub_downloader"><a class="link" href="#_using_the_custom_stub_downloader">Using the Custom Stub Downloader</a></h3>
|
||
<div class="paragraph">
|
||
<p>You can customize the way your stubs are downloaded by creating an implementation of the
|
||
<code>StubDownloaderBuilder</code> interface, 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">package com.example;
|
||
|
||
class CustomStubDownloaderBuilder implements StubDownloaderBuilder {
|
||
|
||
@Override
|
||
public StubDownloader build(final StubRunnerOptions stubRunnerOptions) {
|
||
return new StubDownloader() {
|
||
@Override
|
||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||
StubConfiguration config) {
|
||
File unpackedStubs = retrieveStubs();
|
||
return new AbstractMap.SimpleEntry<>(
|
||
new StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
|
||
config.getClassifier()), unpackedStubs);
|
||
}
|
||
|
||
File retrieveStubs() {
|
||
// here goes your custom logic to provide a folder where all the stubs reside
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Then you can register it in your <code>spring.factories</code> file, as shown in the following
|
||
example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlightjs highlight"><code># Example of a custom Stub Downloader Provider
|
||
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
|
||
com.example.CustomStubDownloaderBuilder</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now you can pick a folder with the source of your stubs.</p>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
If you do not provide any implementation, then the default is used (scan classpath).
|
||
If you provide the <code>stubsMode = StubRunnerProperties.StubsMode.LOCAL</code> or
|
||
<code>, stubsMode = StubRunnerProperties.StubsMode.REMOTE</code> then the Aether implementation will be used
|
||
If you provide more than one, then the first one on the list is used.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="scm-stub-downloader"><a class="link" href="#scm-stub-downloader">Using the SCM Stub Downloader</a></h3>
|
||
<div class="paragraph">
|
||
<p>Whenever the <code>repositoryRoot</code> starts with a SCM protocol
|
||
(currently we support only <code>git://</code>), the stub downloader will try
|
||
to clone the repository and use it as a source of contracts
|
||
to generate tests or stubs.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Either via environment variables, system properties, properties set
|
||
inside the plugin or contracts repository configuration you can
|
||
tweak the downloader’s behaviour. Below you can find the list of
|
||
properties</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all stretch">
|
||
<caption class="title">Table 1. SCM Stub Downloader properties</caption>
|
||
<colgroup>
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 33.3334%;">
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Type of a property</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the property</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>git.branch</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.git.branch</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_GIT_BRANCH</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">master</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Which branch to checkout</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>git.username</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.git.username</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_GIT_USERNAME</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Git clone username</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>git.password</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.git.password</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_GIT_PASSWORD</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Git clone password</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>git.no-of-attempts</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.git.no-of-attempts</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">10</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of attempts to push the commits to <code>origin</code></p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>git.wait-between-attempts</code> (Plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.git.wait-between-attempts</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">1000</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of millis to wait between attempts to push the commits to <code>origin</code></p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="pact-stub-downloader"><a class="link" href="#pact-stub-downloader">Using the Pact Stub Downloader</a></h3>
|
||
<div class="paragraph">
|
||
<p>Whenever the <code>repositoryRoot</code> starts with a Pact protocol
|
||
(starts with <code>pact://</code>), the stub downloader will try
|
||
to fetch the Pact contract definitions from the Pact Broker.
|
||
Whatever is set after <code>pact://</code> will be parsed as the Pact Broker URL.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Either via environment variables, system properties, properties set
|
||
inside the plugin or contracts repository configuration you can
|
||
tweak the downloader’s behaviour. Below you can find the list of
|
||
properties</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all stretch">
|
||
<caption class="title">Table 2. SCM Stub Downloader properties</caption>
|
||
<colgroup>
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 33.3334%;">
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of a property</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Default</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.host</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.host</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_HOST</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Host from URL passed to <code>repositoryRoot</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">What is the URL of Pact Broker</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.port</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.port</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_PORT</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Port from URL passed to <code>repositoryRoot</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">What is the port of Pact Broker</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.protocol</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.protocol</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Protocol from URL passed to <code>repositoryRoot</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">What is the protocol of Pact Broker</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.tags</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.tags</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_TAGS</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Version of the stub, or <code>latest</code> if version is <code>+</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">What tags should be used to fetch the stub</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.auth.scheme</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.auth.scheme</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Basic</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">What kind of authentication should be used to connect to the Pact Broker</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.auth.username</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.auth.username</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The username passed to <code>contractsRepositoryUsername</code> (maven) or <code>contractRepository.username</code> (gradle)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Username used to connect to the Pact Broker</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.auth.password</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.auth.password</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The password passed to <code>contractsRepositoryPassword</code> (maven) or <code>contractRepository.password</code> (gradle)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Password used to connect to the Pact Broker</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">* <code>pactbroker.provider-name-with-group-id</code> (plugin prop)</p>
|
||
<p class="tableblock">* <code>stubrunner.properties.pactbroker.provider-name-with-group-id</code> (system prop)</p>
|
||
<p class="tableblock">* <code>STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID</code> (env prop)</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">When <code>true</code>, the provider name will be a combination of <code>groupId:artifactId</code>. If <code>false</code>, just <code>artifactId</code> is used</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</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> |