700 lines
27 KiB
HTML
700 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="generator" content="Asciidoctor 1.5.7.1">
|
|
<title>Spring Cloud Contract WireMock</title>
|
|
<link rel="stylesheet" href="css/spring.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
|
|
<style>
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.switch {
|
|
border-width: 1px 1px 0 1px;
|
|
border-style: solid;
|
|
border-color: #7a2518;
|
|
display: inline-block;
|
|
}
|
|
|
|
.switch--item {
|
|
padding: 10px;
|
|
background-color: #ffffff;
|
|
color: #7a2518;
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.switch--item:not(:first-child) {
|
|
border-width: 0 0 0 1px;
|
|
border-style: solid;
|
|
border-color: #7a2518;
|
|
}
|
|
|
|
.switch--item.selected {
|
|
background-color: #7a2519;
|
|
color: #ffffff;
|
|
}
|
|
</style>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
|
|
<script type="text/javascript">
|
|
function addBlockSwitches() {
|
|
$('.primary').each(function() {
|
|
primary = $(this);
|
|
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
|
|
primary.children('.title').remove();
|
|
});
|
|
$('.secondary').each(function(idx, node) {
|
|
secondary = $(node);
|
|
primary = findPrimary(secondary);
|
|
switchItem = createSwitchItem(secondary, primary.children('.switch'));
|
|
switchItem.content.addClass('hidden');
|
|
findPrimary(secondary).append(switchItem.content);
|
|
secondary.remove();
|
|
});
|
|
}
|
|
|
|
function createBlockSwitch(primary) {
|
|
blockSwitch = $('<div class="switch"></div>');
|
|
primary.prepend(blockSwitch);
|
|
return blockSwitch;
|
|
}
|
|
|
|
function findPrimary(secondary) {
|
|
candidate = secondary.prev();
|
|
while (!candidate.is('.primary')) {
|
|
candidate = candidate.prev();
|
|
}
|
|
return candidate;
|
|
}
|
|
|
|
function createSwitchItem(block, blockSwitch) {
|
|
blockName = block.children('.title').text();
|
|
content = block.children('.content').first().append(block.next('.colist'));
|
|
item = $('<div class="switch--item">' + blockName + '</div>');
|
|
item.on('click', '', content, function(e) {
|
|
$(this).addClass('selected');
|
|
$(this).siblings().removeClass('selected');
|
|
e.data.siblings('.content').addClass('hidden');
|
|
e.data.removeClass('hidden');
|
|
});
|
|
blockSwitch.append(item);
|
|
return {'item': item, 'content': content};
|
|
}
|
|
|
|
$(addBlockSwitches);
|
|
</script>
|
|
|
|
</head>
|
|
<body class="book toc2 toc-left">
|
|
<div id="header">
|
|
<div id="toc" class="toc2">
|
|
<div id="toctitle">Table of Contents</div>
|
|
<ul class="sectlevel1">
|
|
<li><a href="#_spring_cloud_contract_wiremock">Spring Cloud Contract WireMock</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#_registering_stubs_automatically">Registering Stubs Automatically</a></li>
|
|
<li><a href="#_using_files_to_specify_the_stub_bodies">Using Files to Specify the Stub Bodies</a></li>
|
|
<li><a href="#_alternative_using_junit_rules">Alternative: Using JUnit Rules</a></li>
|
|
<li><a href="#_relaxed_ssl_validation_for_rest_template">Relaxed SSL Validation for Rest Template</a></li>
|
|
<li><a href="#_wiremock_and_spring_mvc_mocks">WireMock and Spring MVC Mocks</a></li>
|
|
<li><a href="#_customization_of_wiremock_configuration">Customization of WireMock configuration</a></li>
|
|
<li><a href="#_generating_stubs_using_rest_docs">Generating Stubs using REST Docs</a></li>
|
|
<li><a href="#_generating_contracts_by_using_rest_docs">Generating Contracts by Using REST Docs</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div id="content">
|
|
<div class="sect1">
|
|
<h2 id="_spring_cloud_contract_wiremock"><a class="link" href="#_spring_cloud_contract_wiremock">Spring Cloud Contract WireMock</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>The Spring Cloud Contract WireMock modules let you use <a href="https://github.com/tomakehurst/wiremock">WireMock</a> in a
|
|
Spring Boot application. Check out the
|
|
<a href="https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples">samples</a>
|
|
for more details.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you have a Spring Boot application that uses Tomcat as an embedded server (which is
|
|
the default with <code>spring-boot-starter-web</code>), you can add
|
|
<code>spring-cloud-starter-contract-stub-runner</code> to your classpath and add <code>@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>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
|
@AutoConfigureWireMock(port = 0)
|
|
public class WiremockForDocsTests {
|
|
|
|
// A service that calls out over HTTP
|
|
@Autowired
|
|
private Service service;
|
|
|
|
@Before
|
|
public void setup() {
|
|
this.service.setBase("http://localhost:"
|
|
+ this.environment.getProperty("wiremock.server.port"));
|
|
}
|
|
|
|
// Using the WireMock APIs in the normal way:
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
// Stubbing WireMock
|
|
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
|
|
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
|
// We're asserting if WireMock responded properly
|
|
assertThat(this.service.go()).isEqualTo("Hello World!");
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To start the stub server on a different port use (for example),
|
|
<code>@AutoConfigureWireMock(port=9999)</code>. For a random port, use a value of <code>0</code>. The stub
|
|
server port can be bound in the test application context with the "wiremock.server.port"
|
|
property. Using <code>@AutoConfigureWireMock</code> adds a bean of type <code>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>WireMockServer</code> into your test.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_registering_stubs_automatically"><a class="link" href="#_registering_stubs_automatically">Registering Stubs Automatically</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you use <code>@AutoConfigureWireMock</code>, it registers WireMock JSON stubs from the file
|
|
system or classpath (by default, from <code>file:src/test/resources/mappings</code>). You can
|
|
customize the locations using the <code>stubs</code> attribute in the annotation, which can be an
|
|
Ant-style resource pattern or a directory. In the case of a directory, <code><strong>*/</strong>.json</code> is
|
|
appended. The following code shows an example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre>@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>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
Actually, WireMock always loads mappings from <code>src/test/resources/mappings</code> <strong>as
|
|
well as</strong> 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.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you’re using Spring Cloud Contract’s default stub jars, then your
|
|
stubs are stored under <code>/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>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@AutoConfigureWireMock(port = 0, stubs = "classpath*:/META-INF/**/mappings/**/*.json")</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_using_files_to_specify_the_stub_bodies"><a class="link" href="#_using_files_to_specify_the_stub_bodies">Using Files to Specify the Stub Bodies</a></h3>
|
|
<div class="paragraph">
|
|
<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>bodyFileName</code> instead of a
|
|
(literal) <code>body</code>. The files are resolved relative to a root directory (by default,
|
|
<code>src/test/resources/__files</code>). To customize this location you can set the <code>files</code>
|
|
attribute in the <code>@AutoConfigureWireMock</code> annotation to the location of the parent
|
|
directory (in other words, <code>__files</code> is a subdirectory). You can use Spring resource
|
|
notation to refer to <code>file:…​</code> or <code>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>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
When you configure the <code>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>files</code> has no
|
|
effect on the stubs loaded explicitly from the <code>stubs</code> attribute.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_alternative_using_junit_rules"><a class="link" href="#_alternative_using_junit_rules">Alternative: Using JUnit Rules</a></h3>
|
|
<div class="paragraph">
|
|
<p>For a more conventional WireMock experience, you can use JUnit <code>@Rules</code> to start and stop
|
|
the server. To do so, use the <code>WireMockSpring</code> convenience class to obtain an <code>Options</code>
|
|
instance, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
|
public class WiremockForDocsClassRuleTests {
|
|
|
|
// Start WireMock on some dynamic port
|
|
// for some reason `dynamicPort()` is not working properly
|
|
@ClassRule
|
|
public static WireMockClassRule wiremock = new WireMockClassRule(
|
|
WireMockSpring.options().dynamicPort());
|
|
|
|
// A service that calls out over HTTP to wiremock's port
|
|
@Autowired
|
|
private Service service;
|
|
|
|
@Before
|
|
public void setup() {
|
|
this.service.setBase("http://localhost:" + wiremock.port());
|
|
}
|
|
|
|
// Using the WireMock APIs in the normal way:
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
// Stubbing WireMock
|
|
wiremock.stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
|
|
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
|
// We're asserting if WireMock responded properly
|
|
assertThat(this.service.go()).isEqualTo("Hello World!");
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The <code>@ClassRule</code> means that the server shuts down after all the methods in this class
|
|
have been run.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_relaxed_ssl_validation_for_rest_template"><a class="link" href="#_relaxed_ssl_validation_for_rest_template">Relaxed SSL Validation for Rest Template</a></h3>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To make this work with minimum fuss, you need to be using the Spring Boot
|
|
<code>RestTemplateBuilder</code> in your app, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
|
|
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
|
return builder.build();
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You need <code>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>@AutoConfigureWireMock</code>
|
|
annotation or the stub runner. If you use the JUnit <code>@Rule</code> approach, you need to add the
|
|
<code>@AutoConfigureHttpClient</code> annotation as well, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest("app.baseUrl=https://localhost:6443")
|
|
@AutoConfigureHttpClient
|
|
public class WiremockHttpsServerApplicationTests {
|
|
|
|
@ClassRule
|
|
public static WireMockClassRule wiremock = new WireMockClassRule(
|
|
WireMockSpring.options().httpsPort(6443));
|
|
...
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you are using <code>spring-boot-starter-test</code>, you have the Apache HTTP client on the
|
|
classpath and it is selected by the <code>RestTemplateBuilder</code> and configured to ignore SSL
|
|
errors. If you use the default <code>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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To disable the custom <code>RestTemplateBuilder</code>, set the <code>wiremock.rest-template-ssl-enabled</code>
|
|
property to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_wiremock_and_spring_mvc_mocks"><a class="link" href="#_wiremock_and_spring_mvc_mocks">WireMock and Spring MVC Mocks</a></h3>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into
|
|
a Spring <code>MockRestServiceServer</code>. The following code shows an example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
|
|
public class WiremockForDocsMockServerApplicationTests {
|
|
|
|
@Autowired
|
|
private RestTemplate restTemplate;
|
|
|
|
@Autowired
|
|
private Service service;
|
|
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
// will read stubs classpath
|
|
MockRestServiceServer server = WireMockRestServiceServer.with(this.restTemplate)
|
|
.baseUrl("https://example.org").stubs("classpath:/stubs/resource.json")
|
|
.build();
|
|
// We're asserting if WireMock responded properly
|
|
assertThat(this.service.go()).isEqualTo("Hello World");
|
|
server.verify();
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The <code>baseUrl</code> value is prepended to all mock calls, and the <code>stubs()</code> method takes a stub
|
|
path resource pattern as an argument. In the preceding example, the stub defined at
|
|
<code>/stubs/resource.json</code> is loaded into the mock server. If the <code>RestTemplate</code> is asked to
|
|
visit <code><a href="https://example.org/" class="bare">https://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 href="https://wiremock.org/docs/stubbing/">WireMock website</a>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
<div class="sect2">
|
|
<h3 id="_customization_of_wiremock_configuration"><a class="link" href="#_customization_of_wiremock_configuration">Customization of WireMock configuration</a></h3>
|
|
<div class="paragraph">
|
|
<p>You can register a bean of <code>org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer</code> type
|
|
in order to customize the WireMock configuration (e.g. add custom transformers).
|
|
Example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Bean
|
|
WireMockConfigurationCustomizer optionsCustomizer() {
|
|
return new WireMockConfigurationCustomizer() {
|
|
@Override
|
|
public void customize(WireMockConfiguration options) {
|
|
// perform your customization here
|
|
}
|
|
};
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_generating_stubs_using_rest_docs"><a class="link" href="#_generating_stubs_using_rest_docs">Generating Stubs using REST Docs</a></h3>
|
|
<div class="paragraph">
|
|
<p><a href="https://projects.spring.io/spring-restdocs">Spring REST Docs</a> can be used to generate
|
|
documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc
|
|
or <code>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>@AutoConfigureRestDocs</code> to have stubs be
|
|
automatically generated in the REST Docs output directory. The following code shows an
|
|
example using <code>MockMvc</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest
|
|
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
|
@AutoConfigureMockMvc
|
|
public class ApplicationTests {
|
|
|
|
@Autowired
|
|
private MockMvc mockMvc;
|
|
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
mockMvc.perform(get("/resource"))
|
|
.andExpect(content().string("Hello World"))
|
|
.andDo(document("resource"));
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>WebTestClient</code> (used
|
|
for testing Spring WebFlux applications) would look like this:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@RunWith(SpringRunner.class)
|
|
@SpringBootTest
|
|
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
|
@AutoConfigureWebTestClient
|
|
public class ApplicationTests {
|
|
|
|
@Autowired
|
|
private WebTestClient client;
|
|
|
|
@Test
|
|
public void contextLoads() throws Exception {
|
|
client.get().uri("/resource").exchange()
|
|
.expectBody(String.class).isEqualTo("Hello World")
|
|
.consumeWith(document("resource"));
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>Creating a stub that matches only in the way you specify.</p>
|
|
</li>
|
|
<li>
|
|
<p>Asserting that the request in the test case also matches the same conditions.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The main entry point for this feature is <code>WireMockRestDocs.verify()</code>, which can be used
|
|
as a substitute for the <code>document()</code> convenience method, as shown in the following
|
|
example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre>@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>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>.jsonPath()</code> to add additional
|
|
matchers. If JSON Path is unfamiliar, The <a href="https://github.com/jayway/JsonPath">JayWay
|
|
documentation</a> can help you get up to speed. The <code>WebTestClient</code> version of this test
|
|
has a similar <code>verify()</code> static helper that you insert in the same place.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Instead of the <code>jsonPath</code> and <code>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>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Test
|
|
public void contextLoads() throws Exception {
|
|
mockMvc.perform(post("/resource")
|
|
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
|
|
.andExpect(status().isOk())
|
|
.andDo(verify()
|
|
.wiremock(WireMock.post(
|
|
urlPathEquals("/resource"))
|
|
.withRequestBody(matchingJsonPath("$.id"))
|
|
.stub("post-resource"));
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="title">post-resource.json</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
|
|
"request" : {
|
|
"url" : "/resource",
|
|
"method" : "POST",
|
|
"bodyPatterns" : [ {
|
|
"matchesJsonPath" : "$.id"
|
|
}]
|
|
},
|
|
"response" : {
|
|
"status" : 200,
|
|
"body" : "Hello World",
|
|
"headers" : {
|
|
"X-Application-Context" : "application:-1",
|
|
"Content-Type" : "text/plain"
|
|
}
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
You can use either the <code>wiremock()</code> method or the <code>jsonPath()</code> and <code>contentType()</code>
|
|
methods to create request matchers, but you can’t use both approaches.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>On the consumer side, you can make the <code>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>@AutoConfigureWireMock(stubs="classpath:resource.json")</code>, as described earlier in this
|
|
document.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_generating_contracts_by_using_rest_docs"><a class="link" href="#_generating_contracts_by_using_rest_docs">Generating Contracts by Using REST Docs</a></h3>
|
|
<div class="paragraph">
|
|
<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>
|
|
</div>
|
|
<div class="paragraph">
|
|
<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>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
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.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Consider the following test:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> this.mockMvc
|
|
.perform(post("/foo").accept(MediaType.APPLICATION_PDF)
|
|
.accept(MediaType.APPLICATION_JSON)
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content("{\"foo\": 23, \"bar\" : \"baz\" }"))
|
|
.andExpect(status().isOk()).andExpect(content().string("bar"))
|
|
// first WireMock
|
|
.andDo(WireMockRestDocs.verify().jsonPath("$[?(@.foo >= 20)]")
|
|
.jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]")
|
|
.contentType(MediaType.valueOf("application/json"))
|
|
.stub("shouldGrantABeerIfOldEnough"))
|
|
// then Contract DSL documentation
|
|
.andDo(document("index", SpringCloudContractRestDocs.dslContract()));</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The preceding test creates the stub presented in the previous section, generating both
|
|
the contract and a documentation file.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The contract is called <code>index.groovy</code> and might look like the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">import org.springframework.cloud.contract.spec.Contract
|
|
|
|
Contract.make {
|
|
request {
|
|
method 'POST'
|
|
url '/foo'
|
|
body('''
|
|
{"foo": 23 }
|
|
''')
|
|
headers {
|
|
header('''Accept''', '''application/json''')
|
|
header('''Content-Type''', '''application/json''')
|
|
}
|
|
}
|
|
response {
|
|
status OK()
|
|
body('''
|
|
bar
|
|
''')
|
|
headers {
|
|
header('''Content-Type''', '''application/json;charset=UTF-8''')
|
|
header('''Content-Length''', '''3''')
|
|
}
|
|
testMatchers {
|
|
jsonPath('$[?(@.foo >= 20)]', byType())
|
|
}
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The generated document (formatted in Asciidoc in this case) contains a formatted
|
|
contract. The location of this file would be <code>index/dsl-contract.adoc</code>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript" src="js/tocbot/tocbot.min.js"></script>
|
|
<script type="text/javascript" src="js/toc.js"></script>
|
|
<link rel="stylesheet" href="js/highlight/styles/atom-one-dark-reasonable.min.css">
|
|
<script src="js/highlight/highlight.min.js"></script>
|
|
<script>hljs.initHighlighting()</script>
|
|
</body>
|
|
</html> |