Sync docs from master to gh-pages
This commit is contained in:
@@ -755,11 +755,16 @@ $(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="#_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>
|
||||
<li><a href="#_contract_dsl_2">Contract DSL</a></li>
|
||||
<li><a href="#_base_mock_spec">Base Mock Spec</a></li>
|
||||
</ul>
|
||||
@@ -6391,10 +6396,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">
|
||||
@@ -6419,9 +6427,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
|
||||
@@ -6584,8 +6591,8 @@ String isoTime() {
|
||||
</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>
|
||||
@@ -6690,12 +6697,290 @@ String isoTime() {
|
||||
</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>
|
||||
</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>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>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('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
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 some default regex
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
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("$.alpha", String.class)).matches("[\\p{L}]*");
|
||||
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(class java.lang.String.class);
|
||||
assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
|
||||
assertThat(parsedJson.read("$.valueWithMin", java.util.Collection.class).size()).isLessThanOrEqualTo(1);
|
||||
assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
|
||||
assertThat(parsedJson.read("$.valueWithMax", java.util.Collection.class).size()).isGreaterThanOrEqualTo(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" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]"
|
||||
}, {
|
||||
"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 class="sect4">
|
||||
<h5 id="_contract_dsl_2">Contract DSL</h5>
|
||||
<div class="listingblock">
|
||||
|
||||
Reference in New Issue
Block a user