323 lines
40 KiB
HTML
323 lines
40 KiB
HTML
<html><head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
<title>96. Spring Cloud Contract WireMock</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_contract.html" title="Part XIII. Spring Cloud Contract"><link rel="prev" href="multi__using_the_pluggable_architecture.html" title="95. Using the Pluggable Architecture"><link rel="next" href="multi__migrations.html" title="97. 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">96. Spring Cloud Contract WireMock</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__using_the_pluggable_architecture.html">Prev</a> </td><th width="60%" align="center">Part XIII. Spring Cloud Contract</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><h2 class="title"><a name="_spring_cloud_contract_wiremock" href="#_spring_cloud_contract_wiremock"></a>96. Spring Cloud Contract WireMock</h2></div></div></div><p>The Spring Cloud Contract WireMock modules let you 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 (which is
|
|
the default with <code class="literal">spring-boot-starter-web</code>), you can add
|
|
<code class="literal">spring-cloud-starter-contract-stub-runner</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 behavior using a Java API or via static JSON declarations as part of
|
|
your test. The following code shows 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.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;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Before</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> setup() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.setBase(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span>
|
|
+ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"wiremock.server.port"</span>));
|
|
}
|
|
|
|
<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 (for example),
|
|
<code class="literal">@AutoConfigureWireMock(port=9999)</code>. For a random port, use a value of <code class="literal">0</code>. The stub
|
|
server port can be bound in the test application context with the "wiremock.server.port"
|
|
property. 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, the same as for Spring integration tests. Also you can inject a bean of type <code class="literal">WireMockServer</code> into your test.</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>96.1 Registering Stubs Automatically</h2></div></div></div><p>If you use <code class="literal">@AutoConfigureWireMock</code>, it registers 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 an
|
|
Ant-style resource pattern or a directory. In the case of a directory, <code class="literal"><span class="strong"><strong>*/</strong></span>.json</code> is
|
|
appended. The following code shows an 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 behavior, you can
|
|
also specify a files root as described in the next section of this document.</p></td></tr></table></div><p>If you’re using Spring Cloud Contract’s default stub jars, then your
|
|
stubs are stored under <code class="literal">/META-INF/group-id/artifact-id/versions/mappings/</code> folder. If you want to register all stubs from that location, from all embedded JARs, then it’s enough to use the following syntax.</p><pre class="programlisting">@AutoConfigureWireMock(port = <span class="hl-number">0</span>, stubs = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath*:/META-INF/**/mappings/**/*.json"</span>)</pre></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>96.2 Using Files to Specify the Stub Bodies</h2></div></div></div><p>WireMock can read response bodies from files on the classpath or the file system. In that
|
|
case, you can see in the JSON DSL that the response has a <code class="literal">bodyFileName</code> instead of a
|
|
(literal) <code class="literal">body</code>. The files are resolved relative to a root directory (by default,
|
|
<code class="literal">src/test/resources/__files</code>). 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 (in other words, <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. Generic URLs are not
|
|
supported. A list of values can be given, in which case WireMock resolves 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, it also affects the
|
|
automatic loading of stubs, because 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>96.3 Alternative: Using JUnit Rules</h2></div></div></div><p>For a more conventional WireMock experience, you can use JUnit <code class="literal">@Rules</code> to start and stop
|
|
the server. To do so, use the <code class="literal">WireMockSpring</code> convenience class to obtain an <code class="literal">Options</code>
|
|
instance, as shown in the following 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>
|
|
<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 wiremock's 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;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Before</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> setup() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.setBase(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span> + wiremock.port());
|
|
}
|
|
|
|
<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 <code class="literal">@ClassRule</code> means that the server shuts down after all the methods in this class
|
|
have been run.</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>96.4 Relaxed SSL Validation for Rest Template</h2></div></div></div><p>WireMock lets you stub a "secure" server with an "https" URL protocol. If your
|
|
application wants to contact that stub server in an integration test, it will find that
|
|
the SSL certificates are not valid (the usual problem with self-installed certificates).
|
|
The best option is often to re-configure the client to use "http". If that’s not an
|
|
option, you can ask Spring to configure an HTTP client that ignores SSL validation errors
|
|
(do so only for tests, of course).</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, as shown in the following example:</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>You need <code class="literal">RestTemplateBuilder</code> because the builder is passed through callbacks to
|
|
initialize it, so the SSL validation can be set up in the client at that point. This
|
|
happens automatically in your test if you are using the <code class="literal">@AutoConfigureWireMock</code>
|
|
annotation or the stub runner. If you use the JUnit <code class="literal">@Rule</code> approach, you need to add the
|
|
<code class="literal">@AutoConfigureHttpClient</code> annotation as well, as shown in the following 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("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>, you have the Apache HTTP client on the
|
|
classpath and it is selected by the <code class="literal">RestTemplateBuilder</code> and configured to ignore SSL
|
|
errors. If you use the default <code class="literal">java.net</code> client, you do not 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><p>To disable the custom <code class="literal">RestTemplateBuilder</code>, set the <code class="literal">wiremock.rest-template-ssl-enabled</code>
|
|
property to <code class="literal">false</code>.</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>96.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>. The following code shows 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> value is prepended to all mock calls, and the <code class="literal">stubs()</code> method takes a stub
|
|
path resource pattern as an argument. In the preceding example, the stub defined at
|
|
<code class="literal">/stubs/resource.json</code> is loaded into the mock server. 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 gets the responses as being declared at that URL. More
|
|
than one stub pattern can be specified, and each one can be a directory (for a recursive
|
|
list of all ".json"), a fixed filename (as in the example above), or an Ant-style
|
|
pattern. The JSON format is the normal WireMock format, which you can read about in the
|
|
<a class="link" href="http://wiremock.org/docs/stubbing/" target="_top">WireMock website</a>.</p><p>Currently, the Spring Cloud Contract Verifier supports 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="_customization_of_wiremock_configuration" href="#_customization_of_wiremock_configuration"></a>96.6 Customization of WireMock configuration</h2></div></div></div><p>You can register a bean of <code class="literal">org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer</code> type
|
|
in order to customize the WireMock configuration (e.g. add custom transformers).
|
|
Example:</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
WireMockConfigurationCustomizer optionsCustomizer() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockConfigurationCustomizer() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</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> customize(WireMockConfiguration options) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform your customization here</span>
|
|
}
|
|
};
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_stubs_using_rest_docs" href="#_generating_stubs_using_rest_docs"></a>96.7 Generating Stubs using REST Docs</h2></div></div></div><p><a class="link" href="https://projects.spring.io/spring-restdocs" target="_top">Spring REST Docs</a> can be used to generate
|
|
documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc
|
|
or <code class="literal">WebTestClient</code> or Rest Assured. At the same time that you generate documentation for your API, you can also
|
|
generate WireMock stubs by using Spring Cloud Contract WireMock. To do so, write your
|
|
normal REST Docs test cases and use <code class="literal">@AutoConfigureRestDocs</code> to have stubs be
|
|
automatically generated in the REST Docs output directory. The following code shows an
|
|
example using <code class="literal">MockMvc</code>:</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>This test generates a WireMock stub at "target/snippets/stubs/resource.json". It matches
|
|
all GET requests to the "/resource" path. The same example with <code class="literal">WebTestClient</code> (used
|
|
for testing Spring WebFlux applications) would look like this:</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">@AutoConfigureWebTestClient</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> WebTestClient client;
|
|
|
|
<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 {
|
|
client.get().uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>).exchange()
|
|
.expectBody(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>)
|
|
.consumeWith(document(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"resource"</span>));
|
|
}
|
|
}</pre><p>Without any additional configuration, these tests 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. Doing so has two effects:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Creating a stub that matches only in the way you specify.</li><li class="listitem">Asserting that the request in the test case also matches the same conditions.</li></ul></div><p>The main entry point for this feature is <code class="literal">WireMockRestDocs.verify()</code>, which can be used
|
|
as a substitute for the <code class="literal">document()</code> convenience method, as shown in the following
|
|
example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;</pre><pre class="screen">@RunWith(SpringRunner.class)
|
|
@SpringBootTest
|
|
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
|
@AutoConfigureMockMvc
|
|
public class ApplicationTests {
|
|
|
|
@Autowired
|
|
private MockMvc mockMvc;
|
|
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
mockMvc.perform(post("/resource")
|
|
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
|
|
.andExpect(status().isOk())
|
|
.andDo(verify().jsonPath("$.id")
|
|
.stub("resource"));
|
|
}
|
|
}</pre><p>This contract specifies that any valid POST with an "id" field receives the response
|
|
defined in this test. You can chain together calls to <code class="literal">.jsonPath()</code> to add additional
|
|
matchers. If JSON Path is unfamiliar, The <a class="link" href="https://github.com/jayway/JsonPath" target="_top">JayWay
|
|
documentation</a> can help you get up to speed. The <code class="literal">WebTestClient</code> version of this test
|
|
has a similar <code class="literal">verify()</code> static helper that you insert in the same place.</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 that the request matches the created stub, as shown in the
|
|
following 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. These features can be used to create stubs with a wider
|
|
range of parameters. The above example generates a stub resembling the following example:</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 you can’t use both approaches.</p></td></tr></table></div><p>On the consumer side, you can make the <code class="literal">resource.json</code> generated earlier in this section
|
|
available on the classpath (by
|
|
<<publishing-stubs-as-jars], for example). After that, you can create a stub using WireMock in a
|
|
number of different ways, including by using
|
|
<code class="literal">@AutoConfigureWireMock(stubs="classpath:resource.json")</code>, as described earlier in this
|
|
document.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_contracts_by_using_rest_docs" href="#_generating_contracts_by_using_rest_docs"></a>96.8 Generating Contracts by Using REST Docs</h2></div></div></div><p>You can also generate Spring Cloud Contract DSL files and documentation with Spring REST
|
|
Docs. If you do so in combination with Spring Cloud WireMock, you get both the contracts
|
|
and the stubs.</p><p>Why would you want to use this feature? Some people in the community asked questions
|
|
about a 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 lets you generate
|
|
the contract files that you can later modify and move to folders (defined in your
|
|
configuration) so that the plugin finds them.</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. The functionality
|
|
is there because it makes sense to generate both the contracts and the stubs.</p></td></tr></table></div><p>Consider 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, \"bar\" : \"baz\" }"</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>)
|
|
.jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.bar in ['baz','bazz','bazzz'])]"</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>The preceding test creates the stub presented in the previous section, generating both
|
|
the contract and a documentation file.</p><p>The contract is called <code class="literal">index.groovy</code> and might look like the following example:</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 OK()
|
|
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 (formatted in Asciidoc in this case) contains 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__using_the_pluggable_architecture.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_contract.html">Up</a></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">95. Using the Pluggable Architecture </td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top"> 97. Migrations</td></tr></table></div></body></html> |