285 lines
35 KiB
HTML
285 lines
35 KiB
HTML
<html><head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
<title>10. Spring Cloud Contract WireMock</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="multi_spring-cloud-contract.html" title="Spring Cloud Contract"><link rel="up" href="multi_spring-cloud-contract.html" title="Spring Cloud Contract"><link rel="prev" href="multi__pluggable_architecture.html" title="9. Pluggable architecture"><link rel="next" href="multi__migrations.html" title="11. Migrations"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">10. Spring Cloud Contract WireMock</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__pluggable_architecture.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="multi__migrations.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_contract_wiremock" href="#_spring_cloud_contract_wiremock"></a>10. Spring Cloud Contract WireMock</h1></div></div></div><p>Modules giving you the possibility to use
|
|
<a class="link" href="http://wiremock.org" target="_top">WireMock</a> in a Spring Boot application. Check out the
|
|
<a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples" target="_top">samples</a>
|
|
for more details.</p><p>If you have a Spring Boot application that uses Tomcat as an embedded
|
|
server, for example (the default with <code class="literal">spring-boot-starter-web</code>), then
|
|
you can simply add <code class="literal">spring-cloud-contract-wiremock</code> to your classpath
|
|
and add <code class="literal">@AutoConfigureWireMock</code> in order to be able to use Wiremock
|
|
in your tests. Wiremock runs as a stub server and you can register
|
|
stub behaviour using a Java API or via static JSON declarations as
|
|
part of your test. Here’s a simple example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@AutoConfigureWireMock(port = 0)</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockForDocsTests {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// A service that calls out over HTTP</span>
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Service service;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Using the WireMock APIs in the normal way:</span>
|
|
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Stubbing WireMock</span>
|
|
stubFor(get(urlEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>))
|
|
.willReturn(aResponse().withHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>).withBody(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>)));
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// We're asserting if WireMock responded properly</span>
|
|
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.go()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>);
|
|
}
|
|
|
|
}</pre><p>To start the stub server on a different port use <code class="literal">@AutoConfigureWireMock(port=9999)</code> (for example), and for a random port use the value 0. The stub server port will be bindable in the test application context as "wiremock.server.port". Using <code class="literal">@AutoConfigureWireMock</code> adds a bean of type <code class="literal">WiremockConfiguration</code> to your test application context, where it will be cached in between methods and classes having the same context, just like for normal Spring integration tests.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_stubs_automatically" href="#_registering_stubs_automatically"></a>10.1 Registering Stubs Automatically</h2></div></div></div><p>If you use <code class="literal">@AutoConfigureWireMock</code> then it will register WireMock
|
|
JSON stubs from the file system or classpath, by default from
|
|
<code class="literal">file:src/test/resources/mappings</code>. You can customize the locations
|
|
using the <code class="literal">stubs</code> attribute in the annotation, which can be a resource
|
|
pattern (ant-style) or a directory, in which case <code class="literal"><span class="strong"><strong>*/</strong></span>.json</code> is
|
|
appended. Example:</p><pre class="screen">@RunWith(SpringRunner.class)
|
|
@SpringBootTest
|
|
@AutoConfigureWireMock(stubs="classpath:/stubs")
|
|
public class WiremockImportApplicationTests {
|
|
|
|
@Autowired
|
|
private Service service;
|
|
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
assertThat(this.service.go()).isEqualTo("Hello World!");
|
|
}
|
|
|
|
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Actually WireMock always loads mappings from
|
|
<code class="literal">src/test/resources/mappings</code> <span class="strong"><strong>as well as</strong></span> the custom locations in the
|
|
stubs attribute. To change this behaviour you have to also specify a
|
|
files root as described next.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_files_to_specify_the_stub_bodies" href="#_using_files_to_specify_the_stub_bodies"></a>10.2 Using Files to Specify the Stub Bodies</h2></div></div></div><p>WireMock can read response bodies from files on the classpath or file
|
|
system. In that case you will see in the JSON DSL that the response
|
|
has a "bodyFileName" instead of a (literal) "body". The files are
|
|
resolved relative to a root directory <code class="literal">src/test/resources/__files</code> by
|
|
default. To customize this location you can set the <code class="literal">files</code> attribute
|
|
in the <code class="literal">@AutoConfigureWireMock</code> annotation to the location of the
|
|
parent directory (i.e. the place <code class="literal">__files</code> is a
|
|
subdirectory). You can use Spring resource notation to refer to
|
|
<code class="literal">file:…​</code> or <code class="literal">classpath:…​</code> locations (but generic URLs are not
|
|
supported). A list of values can be given and WireMock will resolve
|
|
the first file that exists when it needs to find a response body.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>when you configure the <code class="literal">files</code> root, then it affects the
|
|
automatic loading of stubs as well (they come from the root location
|
|
in a subdirectory called "mappings"). The value of <code class="literal">files</code> has no
|
|
effect on the stubs loaded explicitly from the <code class="literal">stubs</code> attribute.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_alternative_using_junit_rules" href="#_alternative_using_junit_rules"></a>10.3 Alternative: Using JUnit Rules</h2></div></div></div><p>For a more conventional WireMock experience, using JUnit <code class="literal">@Rules</code> to
|
|
start and stop the server, just use the <code class="literal">WireMockSpring</code> convenience
|
|
class to obtain an <code class="literal">Options</code> instance:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockForDocsClassRuleTests {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start WireMock on some dynamic port</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// for some reason `dynamicPort()` is not working properly</span>
|
|
<em><span class="hl-annotation" style="color: gray">@ClassRule</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> WireMockClassRule wiremock = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockClassRule(
|
|
WireMockSpring.options().dynamicPort());
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// A service that calls out over HTTP to localhost:${wiremock.port}</span>
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Service service;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Using the WireMock APIs in the normal way:</span>
|
|
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Stubbing WireMock</span>
|
|
wiremock.stubFor(get(urlEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>))
|
|
.willReturn(aResponse().withHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>).withBody(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>)));
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// We're asserting if WireMock responded properly</span>
|
|
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.go()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>);
|
|
}
|
|
|
|
}</pre><p>The use <code class="literal">@ClassRule</code> means that the server will shut down after all the methods in this class.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_relaxed_ssl_validation_for_rest_template" href="#_relaxed_ssl_validation_for_rest_template"></a>10.4 Relaxed SSL Validation for Rest Template</h2></div></div></div><p>WireMock allows you to stub a "secure" server with an "https" URL protocol. If your application wants to
|
|
contact that stub server in an integration test, then it will find that the SSL certificates are not
|
|
valid (it’s the usual problem with self-installed certificates). The best option is often to just
|
|
re-configure the client to use "http", but if that’s not open to you then you can ask Spring to configure
|
|
an HTTP client that ignores SSL validation errors (just for tests).</p><p>To make this work with minimum fuss you need to be using the Spring Boot <code class="literal">RestTemplateBuilder</code> in your app,
|
|
e.g.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> RestTemplate restTemplate(RestTemplateBuilder builder) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> builder.build();
|
|
}</pre><p>This is because the builder is passed through callbacks to initalize it, so the SSL validation can be set up
|
|
in the client at that point. This will happen automatically in your test if you are using the
|
|
<code class="literal">@AutoConfigureWireMock</code> annotation (or the stub runner). If you are using the JUnit <code class="literal">@Rule</code> approach you need
|
|
to add the <code class="literal">@AutoConfigureHttpClient</code> annotation as well:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootTest("app.baseUrl=https://localhost:6443")</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@AutoConfigureHttpClient</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockHttpsServerApplicationTests {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@ClassRule</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> WireMockClassRule wiremock = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockClassRule(
|
|
WireMockSpring.options().httpsPort(<span class="hl-number">6443</span>));
|
|
...
|
|
}</pre><p>If you are using <code class="literal">spring-boot-starter-test</code> then you will have the Apache HTTP client on the classpath and it will
|
|
be selected by the <code class="literal">RestTemplateBuilder</code> and configured to ignore SSL errors. If you are using the default <code class="literal">java.net</code>
|
|
client you don’t need the annotation (but it won’t do any harm). There is no support currently for other clients, but
|
|
it may be added in future releases.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_wiremock_and_spring_mvc_mocks" href="#_wiremock_and_spring_mvc_mocks"></a>10.5 WireMock and Spring MVC Mocks</h2></div></div></div><p>Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a
|
|
Spring <code class="literal">MockRestServiceServer</code>. Here’s an example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.NONE)</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockForDocsMockServerApplicationTests {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RestTemplate restTemplate;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Service service;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// will read stubs classpath</span>
|
|
MockRestServiceServer server = WireMockRestServiceServer.with(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.restTemplate)
|
|
.baseUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://example.org"</span>).stubs(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath:/stubs/resource.json"</span>)
|
|
.build();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// We're asserting if WireMock responded properly</span>
|
|
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.go()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>);
|
|
server.verify();
|
|
}
|
|
}</pre><p>The <code class="literal">baseUrl</code> is prepended to all mock calls, and the <code class="literal">stubs()</code>
|
|
method takes a stub path resource pattern as an argument. So in this
|
|
example the stub defined at <code class="literal">/stubs/resource.json</code> is loaded into the
|
|
mock server, so if the <code class="literal">RestTemplate</code> is asked to visit
|
|
<code class="literal"><a class="link" href="http://example.org/" target="_top">http://example.org/</a></code> it will get the responses as declared
|
|
there. More than one stub pattern can be specified, and each one can
|
|
be a directory (for a recursive list of all ".json"), or a fixed
|
|
filename (like in the example above) or an ant-style pattern. The JSON
|
|
format is the normal WireMock format which you can read about in the
|
|
WireMock website.</p><p>Currently we support Tomcat, Jetty and Undertow as Spring Boot
|
|
embedded servers, and Wiremock itself has "native" support for a
|
|
particular version of Jetty (currently 9.2). To use the native Jetty
|
|
you need to add the native wiremock dependencies and exclude the
|
|
Spring Boot container if there is one.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_stubs_using_restdocs" href="#_generating_stubs_using_restdocs"></a>10.6 Generating Stubs using RestDocs</h2></div></div></div><p><a class="link" href="https://projects.spring.io/spring-restdocs" target="_top">Spring RestDocs</a> can be
|
|
used to generate documentation (e.g. in asciidoctor format) for an
|
|
HTTP API with Spring MockMvc or Rest Assured. At the same time as you
|
|
generate documentation for your API, you can also generate WireMock
|
|
stubs, by using Spring Cloud Contract WireMock. Just write your normal
|
|
RestDocs test cases and use <code class="literal">@AutoConfigureRestDocs</code> to have stubs
|
|
automatically in the restdocs output directory. For example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@AutoConfigureRestDocs(outputDir = "target/snippets")</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@AutoConfigureMockMvc</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ApplicationTests {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MockMvc mockMvc;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
mockMvc.perform(get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>))
|
|
.andExpect(content().string(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>))
|
|
.andDo(document(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"resource"</span>));
|
|
}
|
|
}</pre><p>From this test will be generated a WireMock stub at
|
|
"target/snippets/stubs/resource.json". It matches all GET requests to
|
|
the "/resource" path.</p><p>Without any additional configuration this will create a stub with a
|
|
request matcher for the HTTP method and all headers except "host" and
|
|
"content-length". To match the request more precisely, for example to
|
|
match the body of a POST or PUT, we need to explicitly create a
|
|
request matcher. This will do two things: 1) create a stub that only
|
|
matches the way you specify, 2) assert that the request in the test
|
|
case also matches the same conditions.</p><p>The main entry point for this is <code class="literal">WireMockRestDocs.verify()</code> which can
|
|
be used as a substitute for the <code class="literal">document()</code> convenience method. For
|
|
example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@AutoConfigureRestDocs(outputDir = "target/snippets")</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@AutoConfigureMockMvc</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ApplicationTests {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MockMvc mockMvc;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
mockMvc.perform(post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>)
|
|
.content(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"id\":\"123456\",\"message\":\"Hello World\"}"</span>))
|
|
.andExpect(status().isOk())
|
|
.andDo(verify().jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.id"</span>)
|
|
.stub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"resource"</span>));
|
|
}
|
|
}</pre><p>So this contract is saying: any valid POST with an "id" field will get
|
|
back an the same response as in this test. You can chain together
|
|
calls to <code class="literal">.jsonPath()</code> to add additional matchers. The
|
|
<a class="link" href="https://github.com/jayway/JsonPath" target="_top">JayWay documentation</a> can help you
|
|
to get up to speed with JSON Path if it is unfamiliar to you.</p><p>Instead of the <code class="literal">jsonPath</code> and <code class="literal">contentType</code> convenience methods, you
|
|
can also use the WireMock APIs to verify the request matches the
|
|
created stub. Example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
mockMvc.perform(post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>)
|
|
.content(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"id\":\"123456\",\"message\":\"Hello World\"}"</span>))
|
|
.andExpect(status().isOk())
|
|
.andDo(verify()
|
|
.wiremock(WireMock.post(
|
|
urlPathEquals(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>))
|
|
.withRequestBody(matchingJsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.id"</span>))
|
|
.stub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"post-resource"</span>));
|
|
}</pre><p>The WireMock API is rich - you can match headers, query parameters,
|
|
and request body by regex as well as by json path - so this can useful
|
|
to create stubs with a wider range of parameters. The above example
|
|
will generate a stub something like this:</p><p><b>post-resource.json. </b>
|
|
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.id"</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}]</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-Application-Context"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application:-1"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
|
|
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You can use either the <code class="literal">wiremock()</code> method or the <code class="literal">jsonPath()</code>
|
|
and <code class="literal">contentType()</code> methods to create request matchers, but not both.</p></td></tr></table></div><p>On the consumer side, you can make the <code class="literal">resource.json</code> generated above
|
|
available on the classpath (by <a class="link" href="https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_publishing_stubs_as_jars" target="_top">publishing stubs as JARs</a> for example).
|
|
After that, you can create a stub using WireMock in a
|
|
number of different ways, including as described above using
|
|
<code class="literal">@AutoConfigureWireMock(stubs="classpath:resource.json")</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_contracts_using_restdocs" href="#_generating_contracts_using_restdocs"></a>10.7 Generating Contracts using RestDocs</h2></div></div></div><p>Another thing that can be generated with Spring RestDocs is the Spring Cloud
|
|
Contract DSL file and documentation. If you combine that with Spring Cloud
|
|
WireMock then you’re getting both the contracts and stubs.</p><p>Why would you want to use this feature? Some people in the community asked questions
|
|
about situation in which they would like to move to DSL based contract definition
|
|
but they already have a lot of Spring MVC tests. Using this feature allows you to generate
|
|
the contract files that you can later modify and move to proper folders so that the
|
|
plugin picks them up.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You might wonder why this functionality is in the WireMock module.
|
|
Come to think of it, it does make sense since it makes little sense to generate
|
|
only contracts and not generate the stubs. That’s why we suggest to do both.</p></td></tr></table></div><p>Let’s imagine the following test:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.mockMvc.perform(post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>)
|
|
.accept(MediaType.APPLICATION_PDF)
|
|
.accept(MediaType.APPLICATION_JSON)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\": 23 }"</span>))
|
|
.andExpect(status().isOk())
|
|
.andExpect(content().string(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>))
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// first WireMock</span>
|
|
.andDo(WireMockRestDocs.verify()
|
|
.jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.foo >= 20)]"</span>)
|
|
.contentType(MediaType.valueOf(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>))
|
|
.stub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"shouldGrantABeerIfOldEnough"</span>))
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then Contract DSL documentation</span>
|
|
.andDo(document(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"index"</span>, SpringCloudContractRestDocs.dslContract()));</pre><p>This will lead in the creation of the stub as presented in the previous
|
|
section, contract will get generated and a documentation file too.</p><p>The contract will be called <code class="literal">index.groovy</code> and look more like this.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
|
|
|
|
Contract.make {
|
|
request {
|
|
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
|
|
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo'</span>
|
|
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
|
|
</span> {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>: <span class="hl-number">23</span> }
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">')
|
|
</span> headers {
|
|
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Accept'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
|
|
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
|
|
}
|
|
}
|
|
response {
|
|
status <span class="hl-number">200</span>
|
|
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
|
|
</span> bar
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">')
|
|
</span> headers {
|
|
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json;charset=UTF-8'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
|
|
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Length'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'3'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
|
|
}
|
|
testMatchers {
|
|
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$[?(@.foo >= 20)]'</span>, byType())
|
|
}
|
|
}
|
|
}</pre><p>the generated document (example for Asciidoc) will contain a formatted contract
|
|
(the location of this file would be <code class="literal">index/dsl-contract.adoc</code>).</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__pluggable_architecture.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="multi__migrations.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">9. Pluggable architecture </td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud-contract.html">Home</a></td><td width="40%" align="right" valign="top"> 11. Migrations</td></tr></table></div></body></html> |