Sync docs from 1.0.x to gh-pages

This commit is contained in:
Marcin Grzejszczak
2017-01-13 18:10:30 +01:00
parent 5d8e0699d9
commit e3e7d6e61c

View File

@@ -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 -&gt; {
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&#8217;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>
&lt;properties&gt;
&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&lt;java.version&gt;1.8&lt;/java.version&gt;
&lt;spring-cloud-contract.version&gt;1.1.0.BUILD-SNAPSHOT&lt;/spring-cloud-contract.version&gt;
&lt;spring-cloud-dependencies.version&gt;Dalston.BUILD-SNAPSHOT&lt;/spring-cloud-dependencies.version&gt;
&lt;spring-cloud-contract.version&gt;1.0.4.BUILD-SNAPSHOT&lt;/spring-cloud-contract.version&gt;
&lt;spring-cloud-dependencies.version&gt;Camden.BUILD-SNAPSHOT&lt;/spring-cloud-dependencies.version&gt;
&lt;/properties&gt;
&lt;dependencyManagement&gt;
@@ -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&#8217;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&#8217;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&#8217;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&#8217;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\\/_:@=.+?,##%&amp;~-]*[^.|\\\'|\\# |!|\\(|?|,| |&gt;|&lt;|;|\\)])')
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&#8217;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&#8217;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&#8217;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(&#8230;&#8203;)</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(&#8230;&#8203;)</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&#8217;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 &amp; max 3)
minOccurrence(1)
maxOccurrence(3)
})
}
headers {
contentType(applicationJson())
}
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this example we&#8217;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&#8217;re setting
explicitly the values of regular expressions we&#8217;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&#8217;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&#8217;re checking 4 fields in the way that we&#8217;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&#8217;re just checking the whether the type is the same</p>
</li>
<li>
<p>for <code>$.valueWithMin</code> - we&#8217;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&#8217;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&#8217;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&#8217;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">&lt;plugin&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-maven-plugin&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;extensions&gt;true&lt;/extensions&gt;
&lt;configuration&gt;
&lt;testMode&gt;EXPLICIT&lt;/testMode&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;</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&#8217;ll generate a test that <strong>DOES NOT</strong> use MockMvc. It means that you&#8217;re generating
real requests and you need to setup your generated test&#8217;s base class to work on a real socket.</p>
</div>
<div class="paragraph">
<p>Let&#8217;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&#8217;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>