Sync docs from 1.0.x to gh-pages
This commit is contained in:
523
1.0.x/index.html
523
1.0.x/index.html
@@ -636,6 +636,7 @@ $(addBlockSwitches);
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_running_stubs">Running stubs</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_limitations">Limitations</a></li>
|
||||
<li><a href="#_running_using_main_app">Running using main app</a></li>
|
||||
<li><a href="#_http_stubs">HTTP Stubs</a></li>
|
||||
<li><a href="#_viewing_registered_mappings">Viewing registered mappings</a></li>
|
||||
@@ -743,7 +744,7 @@ $(addBlockSwitches);
|
||||
</li>
|
||||
<li><a href="#_contract_dsl">Contract DSL</a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_limitations">Limitations</a></li>
|
||||
<li><a href="#_limitations_2">Limitations</a></li>
|
||||
<li><a href="#_common_top_level_elements">Common Top-Level elements</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_description">Description</a></li>
|
||||
@@ -753,17 +754,21 @@ $(addBlockSwitches);
|
||||
<li><a href="#_http_top_level_elements">HTTP 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="#_dynamic_properties">Dynamic properties</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_dynamic_properties_inside_the_body">Dynamic properties inside the body</a>
|
||||
<ul class="sectlevel5">
|
||||
<li><a href="#_regular_expressions">Regular expressions</a></li>
|
||||
<li><a href="#_passing_optional_parameters">Passing optional parameters</a></li>
|
||||
<li><a href="#_executing_custom_methods_on_server_side">Executing custom methods on server side</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_contract_dsl_2">Contract DSL</a></li>
|
||||
<li><a href="#_base_mock_spec">Base Mock Spec</a></li>
|
||||
<li><a href="#_executing_custom_methods_on_server_side">Executing custom methods on server side</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_dynamic_properties_in_matchers_sections">Dynamic properties in matchers sections</a></li>
|
||||
</ul>
|
||||
</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="#_messaging_top_level_elements">Messaging Top-Level Elements</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_output_triggered_by_a_method">Output triggered by a method</a></li>
|
||||
@@ -799,7 +804,7 @@ $(addBlockSwitches);
|
||||
Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer</em></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>1.0.3.BUILD-SNAPSHOT</p>
|
||||
<p>1.0.4.BUILD-SNAPSHOT</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -821,7 +826,7 @@ and consumers, for HTTP and message-based interactions.</p>
|
||||
<p>Modules giving you the possibility to use
|
||||
<a href="http://wiremock.org">WireMock</a> with different servers by using the
|
||||
"ambient" server embedded in a Spring Boot application. Check out the
|
||||
<a href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples">samples</a>
|
||||
<a href="https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/samples">samples</a>
|
||||
for more details.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
@@ -1260,7 +1265,7 @@ Full test is generated by Spring Cloud Contract Verifier.</p>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/docs/src/main/asciidoc/images/Deps.png" alt="Microservices Architecture">
|
||||
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/docs/src/main/asciidoc/images/Deps.png" alt="Microservices Architecture">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -1354,7 +1359,7 @@ to set up the whole world of microservices.</p>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/docs/src/main/asciidoc/images/Stubs1.png" alt="Stubbed Services">
|
||||
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/docs/src/main/asciidoc/images/Stubs1.png" alt="Stubbed Services">
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
@@ -1362,7 +1367,7 @@ to set up the whole world of microservices.</p>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/docs/src/main/asciidoc/images/Stubs2.png" alt="Stubbed Services">
|
||||
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/docs/src/main/asciidoc/images/Stubs2.png" alt="Stubbed Services">
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
@@ -1453,7 +1458,7 @@ for response verification.</p>
|
||||
going through the process. CDC is all about communication.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/dsl/http-server">server side code is available here</a> and <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/dsl/http-client">the client side code here</a>.</p>
|
||||
<p>The <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/samples/standalone/dsl/http-server">server side code is available here</a> and <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/samples/standalone/dsl/http-client">the client side code here</a>.</p>
|
||||
</div>
|
||||
<div class="admonitionblock tip">
|
||||
<table>
|
||||
@@ -1973,28 +1978,16 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr</co
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-java" data-lang="java">package com.example.fraud;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import com.example.fraud.FraudDetectionController;
|
||||
import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
public class FraudBase {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
RestAssuredMockMvc.standaloneSetup(new FraudDetectionController(),
|
||||
new FraudStatsController(stubbedStatsProvider()));
|
||||
}
|
||||
|
||||
private StatsProvider stubbedStatsProvider() {
|
||||
return fraudType -> {
|
||||
switch (fraudType) {
|
||||
case DRUNKS:
|
||||
return 100;
|
||||
case ALL:
|
||||
return 200;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
RestAssuredMockMvc.standaloneSetup(new FraudDetectionController());
|
||||
}
|
||||
|
||||
public void assertThatRejectionReasonIsNull(Object rejectionReason) {
|
||||
@@ -2495,7 +2488,7 @@ consumer will you break with your local changes.</p>
|
||||
<div class="paragraph">
|
||||
<p>Let’s assume that we have a producer with coordinates <code>com.example:server</code> and 3 consumers: <code>client1</code>,
|
||||
<code>client2</code>, <code>client3</code>. Then in the repository with common contracts you would have the following setup
|
||||
(which you can checkout <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/contracts">here</a>:</p>
|
||||
(which you can checkout <a href="https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/samples/standalone/contracts">here</a>:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
@@ -2550,8 +2543,8 @@ one to one to the contents of the repo.</p>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-contract.version>1.1.0.BUILD-SNAPSHOT</spring-cloud-contract.version>
|
||||
<spring-cloud-dependencies.version>Dalston.BUILD-SNAPSHOT</spring-cloud-dependencies.version>
|
||||
<spring-cloud-contract.version>1.0.4.BUILD-SNAPSHOT</spring-cloud-contract.version>
|
||||
<spring-cloud-dependencies.version>Camden.BUILD-SNAPSHOT</spring-cloud-dependencies.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -4321,6 +4314,25 @@ For messaging, special stub routes are defined.</p>
|
||||
<div class="sect3">
|
||||
<h4 id="_running_stubs">Running stubs</h4>
|
||||
<div class="sect4">
|
||||
<h5 id="_limitations">Limitations</h5>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Important</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
There might be a problem with StubRunner shutting down ports between tests. You might
|
||||
have a situation in which you get port conflicts. As long as you use the same context across tests
|
||||
everything works fine. But when the context are different (e.g. different stubs or different profiles)
|
||||
then you have to either use <code>@DirtiesContext</code> to shut down the stub servers, or else run them on
|
||||
different ports per test.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_running_using_main_app">Running using main app</h5>
|
||||
<div class="paragraph">
|
||||
<p>You can set the following options to the main class:</p>
|
||||
@@ -6087,7 +6099,7 @@ a tiny subset of it (namely literals, method calls and closures). What’s m
|
||||
</blockquote>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_limitations">Limitations</h4>
|
||||
<h4 id="_limitations_2">Limitations</h4>
|
||||
<div class="admonitionblock warning">
|
||||
<table>
|
||||
<tr>
|
||||
@@ -6371,10 +6383,13 @@ or just set the <code>ignored</code> property on the contract itself:</p>
|
||||
<div class="paragraph">
|
||||
<p>The contract can contain some dynamic properties - timestamps / ids etc. You don’t want to enforce the consumers to stub their
|
||||
clocks to always return the same value of time so that it gets matched by the stub. That’s why we allow you to provide the dynamic
|
||||
parts in your contracts in the following way</p>
|
||||
parts in your contracts in two ways. One is to pass them directly in the
|
||||
body and one to set them in a separate section called <code>testMatchers</code> and <code>stubMatchers</code>.</p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_dynamic_properties_inside_the_body">Dynamic properties inside the body</h5>
|
||||
<div class="paragraph">
|
||||
<p>either via the <code>value</code> method</p>
|
||||
<p>You can set the properties inside the body either via the <code>value</code> method</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
@@ -6399,9 +6414,8 @@ $(client(...), server(...))</code></pre>
|
||||
<p>All of the aforementioned approaches are equal. That means that <code>stub</code> and <code>client</code> methods are aliases over the <code>consumer</code>
|
||||
method. Let’s take a closer look at what we can do with those values in the subsequent sections.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_regular_expressions">Regular expressions</h4>
|
||||
<div class="sect5">
|
||||
<h6 id="_regular_expressions">Regular expressions</h6>
|
||||
<div class="paragraph">
|
||||
<p>You can use regular expressions to write your requests in Contract DSL. It is particularly useful when you want to indicate that a given response
|
||||
should be provided for requests that follow a given pattern. Also, you can use it when you need to use patterns and not exact values both
|
||||
@@ -6478,11 +6492,14 @@ provide the generated string that matches the provided regular expression. For e
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">protected static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
|
||||
protected static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
|
||||
protected static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?')
|
||||
protected static final Pattern IP_ADDRESS = Pattern.compile('([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])');
|
||||
protected static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?');
|
||||
protected static final Pattern IP_ADDRESS = Pattern.compile('([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])')
|
||||
protected static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?')
|
||||
protected static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}');
|
||||
protected static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])')
|
||||
protected static final Pattern UUID = Pattern.compile('[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}')
|
||||
protected static final Pattern ANY_DATE = Pattern.compile('(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])')
|
||||
protected static final Pattern ANY_DATE_TIME = Pattern.compile('([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])')
|
||||
protected static final Pattern ANY_TIME = Pattern.compile('(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])')
|
||||
|
||||
String onlyAlphaUnicode() {
|
||||
return ONLY_ALPHA_UNICODE.pattern()
|
||||
@@ -6514,6 +6531,18 @@ String url() {
|
||||
|
||||
String uuid(){
|
||||
return UUID.pattern()
|
||||
}
|
||||
|
||||
String isoDate() {
|
||||
return ANY_DATE.pattern()
|
||||
}
|
||||
|
||||
String isoDateTime() {
|
||||
return ANY_DATE_TIME.pattern()
|
||||
}
|
||||
|
||||
String isoTime() {
|
||||
return ANY_TIME.pattern()
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6549,8 +6578,8 @@ String uuid(){
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_passing_optional_parameters">Passing optional parameters</h4>
|
||||
<div class="sect5">
|
||||
<h6 id="_passing_optional_parameters">Passing optional parameters</h6>
|
||||
<div class="paragraph">
|
||||
<p>It is possible to provide optional parameters in your contract. It’s only possible to have optional parameter for the:</p>
|
||||
</div>
|
||||
@@ -6655,14 +6684,15 @@ String uuid(){
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_executing_custom_methods_on_server_side">Executing custom methods on server side</h4>
|
||||
<div class="sect5">
|
||||
<h6 id="_executing_custom_methods_on_server_side">Executing custom methods on server side</h6>
|
||||
<div class="paragraph">
|
||||
<p>It is also possible to define a method call to be executed on the server side during the test. Such a method can be added to the class defined as "baseClassForTests"
|
||||
in the configuration. Please see the examples below:</p>
|
||||
in the configuration. Example:</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><strong>Contract</strong></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_contract_dsl_2">Contract DSL</h5>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">org.springframework.cloud.contract.spec.Contract.make {
|
||||
@@ -6688,9 +6718,9 @@ in the configuration. Please see the examples below:</p>
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><strong>Base class</strong></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_base_mock_spec">Base Mock Spec</h5>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">abstract class BaseMockMvcSpec extends Specification {
|
||||
@@ -6712,6 +6742,302 @@ in the configuration. Please see the examples below:</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_dynamic_properties_in_matchers_sections">Dynamic properties in matchers sections</h5>
|
||||
<div class="paragraph">
|
||||
<p>If you’ve been working with <a href="https://docs.pact.io/">Pact</a> this might seem familiar. Quite a few users
|
||||
are used to having a separation between the body and setting dynamic parts of your contract.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>That’s why you can profit from two separate sections. One is called <code>stubMatchers</code> where you can
|
||||
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. The other is called <code>testMatchers</code> which is present in the <code>response</code> or
|
||||
<code>outputMessage</code> side of the contract.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Currently we support only JSON Path based matchers with the following matching possibilities.
|
||||
For <code>stubMatchers</code>:</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>byEquality()</code> - the value taken from the response via the provided JSON Path needs
|
||||
to be equal to the provided value in the contract</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byRegex(…​)</code> - the value taken from the response via the provided JSON Path needs
|
||||
to match the regex</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byDate()</code> - the value taken from the response via the provided JSON Path needs to
|
||||
match the regex for ISO Date</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byTimestamp()</code> - the value taken from the response via the provided JSON Path needs
|
||||
to match the regex for ISO DateTime</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byTime()</code> - the value taken from the response via the provided JSON Path needs to
|
||||
match the regex for ISO Time</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>For <code>testMatchers</code>:</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>byEquality()</code> - the value taken from the response via the provided JSON Path needs
|
||||
to be equal to the provided value in the contract</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byRegex(…​)</code> - the value taken from the response via the provided JSON Path needs
|
||||
to match the regex</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byDate()</code> - the value taken from the response via the provided JSON Path needs to
|
||||
match the regex for ISO Date</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byTimestamp()</code> - the value taken from the response via the provided JSON Path needs
|
||||
to match the regex for ISO DateTime</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byTime()</code> - the value taken from the response via the provided JSON Path needs to
|
||||
match the regex for ISO Time</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>byType()</code> - the value taken from the response via the provided JSON Path needs to
|
||||
be of the same type as the type defined in the body of the response in the contract.
|
||||
<code>byType</code> can take a closure where you can set <code>minOccurrence</code> and <code>maxOccurrence</code>.
|
||||
That way you can assert on the size of the collection.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Let’s take a look at the following example:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
urlPath '/get'
|
||||
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"
|
||||
])
|
||||
stubMatchers {
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
jsonPath('$.duck', byEquality())
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
}
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
}
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
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",
|
||||
valueWithMin: [
|
||||
1,2,3
|
||||
],
|
||||
valueWithMax: [
|
||||
1,2,3
|
||||
],
|
||||
valueWithMinMax: [
|
||||
1,2,3
|
||||
],
|
||||
])
|
||||
testMatchers {
|
||||
// asserts the jsonpath value against manual regex
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
// asserts the jsonpath value against the provided value
|
||||
jsonPath('$.duck', byEquality())
|
||||
// asserts the jsonpath value against some default regex
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
// asserts vs inbuilt time related regex
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
// asserts that the resulting type is the same as in response body
|
||||
jsonPath('$.valueWithTypeMatch', byType())
|
||||
jsonPath('$.valueWithMin', byType {
|
||||
// results in verification of size of array (min 1)
|
||||
minOccurrence(1)
|
||||
})
|
||||
jsonPath('$.valueWithMax', byType {
|
||||
// results in verification of size of array (max 3)
|
||||
maxOccurrence(3)
|
||||
})
|
||||
jsonPath('$.valueWithMinMax', byType {
|
||||
// results in verification of size of array (min 1 & max 3)
|
||||
minOccurrence(1)
|
||||
maxOccurrence(3)
|
||||
})
|
||||
}
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In this example we’re providing the dynamic portions of the contract in the matchers sections.
|
||||
For the request part you can see that for all fields but <code>valueWithoutAMatcher</code> we’re setting
|
||||
explicitly the values of regular expressions we’d like the stub to contain. For the <code>valueWithoutAMatcher</code>
|
||||
the verification will take place in the same way as without the usage of matchers - the test
|
||||
will perform an equality check in this case.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>For the response side in the <code>testMatchers</code> section we’re defining all the dynamic parts
|
||||
in a similar manner. The only difference is that we have the <code>byType</code> matchers too. In that
|
||||
case we’re checking 4 fields in the way that we’re verifying whether the response from the test
|
||||
has a value whose JSON path matching the given field is of the same type as the one defined in the response body and:</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>for <code>$.valueWithTypeMatch</code> - we’re just checking the whether the type is the same</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>for <code>$.valueWithMin</code> - we’re checking the type and assert if the size is greater or equal to the min occurrence</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>for <code>$.valueWithMax</code> - we’re checking the type and assert if the size is smaller or equal to the max occurrence</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>for <code>$.valueWithMinMax</code> - we’re checking the type and assert if the size is between the min and max occurrence</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The resulting test would look more or less like this (note that we’re separating the autogenerated
|
||||
assertions and the one from matchers with an <code>and</code> section):</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-java" 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\"}");
|
||||
|
||||
// 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+)?");
|
||||
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(parsedJson.read("$.valueWithMin", java.util.Collection.class).size()).isGreaterThanOrEqualTo(1);
|
||||
assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
|
||||
assertThat(parsedJson.read("$.valueWithMax", java.util.Collection.class).size()).isLessThanOrEqualTo(3);
|
||||
assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
|
||||
assertThat(parsedJson.read("$.valueWithMinMax", java.util.Collection.class).size()).isStrictlyBetween(1, 3);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>and the WireMock stub like this:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-json" data-lang="json"> '''
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/get",
|
||||
"method" : "GET",
|
||||
"headers" : {
|
||||
"Content-Type" : {
|
||||
"matches" : "application/json.*"
|
||||
}
|
||||
},
|
||||
"bodyPatterns" : [ {
|
||||
"matchesJsonPath" : "$[?(@.valueWithoutAMatcher == 'foo')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.valueWithTypeMatch == 'string')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$.list.some.nested[?(@.anothervalue == 4)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$.list.someother.nested[?(@.anothervalue == 4)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$.list.someother.nested[?(@.json == 'with value')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.duck == 123)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.alpha == 'abc')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.number =~ /(-?\\\\d*(\\\\.\\\\d+)?)/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"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\\",\\"valueWithMin\\":[1,2,3],\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3]}",
|
||||
"headers" : {
|
||||
"Content-Type" : "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
'''</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_jax_rs_support">JAX-RS support</h4>
|
||||
<div class="paragraph">
|
||||
@@ -6783,6 +7109,107 @@ section a <code>async()</code> method. Example:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_working_with_context_paths">Working with Context Paths</h4>
|
||||
<div class="paragraph">
|
||||
<p>Spring Cloud Contract supports context paths.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Important</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
The only thing that changes in order to fully support context paths is the switch
|
||||
on the <strong>PRODUCER</strong> side. The autogenerated tests need to be using the <strong>EXPLICIT</strong> mode.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The consumer side remains untouched, in order for the generated test to pass you have to switch the <strong>EXPLICIT</strong> mode.</p>
|
||||
</div>
|
||||
<div class="listingblock primary">
|
||||
<div class="title">Maven</div>
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-xml" 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="highlight"><code class="language-groovy" data-lang="groovy">contracts {
|
||||
testMode = 'EXPLICIT'
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>That way you’ll generate a test that <strong>DOES NOT</strong> use MockMvc. It means that you’re generating
|
||||
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>Let’s imagine the following contract:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">org.springframework.cloud.contract.spec.Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url '/my-context-path/url'
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Here is an example of how to set up a base class and Rest Assured for everything to work correctly.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">import com.jayway.restassured.RestAssured;
|
||||
import org.junit.Before;
|
||||
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest(classes = ContextPathTestingBaseClass.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class ContextPathTestingBaseClass {
|
||||
|
||||
@LocalServerPort int port;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
RestAssured.baseURI = "http://localhost";
|
||||
RestAssured.port = this.port;
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>That way all:</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>all your requests in the autogenerated tests will be sent to the real endpoint with your context path included (e.g. <code>/my-context-path/url</code>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>your contracts reflect that you have a context path, thus your generated stubs will also
|
||||
have that information (e.g. in the stubs you’ll see that you have too call <code>/my-context-path/url</code>)</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_messaging_top_level_elements">Messaging Top-Level Elements</h4>
|
||||
<div class="paragraph">
|
||||
<p>The DSL for messaging looks a little bit different than the one that focuses on HTTP.</p>
|
||||
|
||||
Reference in New Issue
Block a user