Updated docs (#48)

* Added docs + tests for spec
* Updated WireMock tests for docs
* Made wiremock tests dynamic
This commit is contained in:
Marcin Grzejszczak
2016-07-22 14:07:44 +02:00
committed by GitHub
parent cc1d20c91b
commit 70acffd9e6
10 changed files with 368 additions and 40 deletions

View File

@@ -13,10 +13,125 @@ and consumers, for HTTP and message-based interactions.
=== Spring Cloud Contract WireMock
Modules giving you the possibility to use http://wiremock.org[WireMock] with different servers. Check out the
https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples[samples] for more information.
:core_path: ../../../..
:doc_samples: {core_path}/samples/wiremock-jetty
Currently we support Jetty, Native WireMock server, Tomcat and Undertow.
Modules giving you the possibility to use
http://wiremock.org[WireMock] with different servers by using the
"ambient" server embedded in a Spring Boot application. Check out the
https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples[samples]
for more details.
If you have a Spring Boot application that uses Tomcat as an embedded
server, for example (the default with `spring-boot-starter-web`), then
you can simply add `spring-cloud-contract-wiremock` to your classpath
and add `@AutoConfigureWireMock` 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:
[source,java,indent=0]
----
@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;
// 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!");
}
}
----
To start the stub server on a different port use `@AutoConfigureWireMock(port=9999)` (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 `@AutoConfigureWireMock` adds a bean of type `WiremockConfiguration` 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.
For a more conventional WireMock experience, using JUnit `@Rules` to
start and stop the server, just use the `WireMockSpring` convenience
class to obtain an `Options` instance:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock
public class WiremockForDocsClassRuleTests {
// Start WireMock on some dynamic port
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().dynamicPort());
// A service that calls out over HTTP to localhost:${wiremock.port}
@Autowired
private Service service;
// 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!");
}
}
----
The use `@ClassRule` means that the server will shut down after all the methods in this class.
== WireMock and Spring MVC Mocks
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a
Spring `MockRestServiceServer`. Here's an example:
[source,java,indent=0]
----
@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 from default /resources/stubs location
MockRestServiceServer server = WireMockExpectations.with(this.restTemplate)
.baseUrl("http://example.org")
.expect("resource");
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World");
server.verify();
}
}
----
The `baseUrl` is prepended to all mock calls, and the `expect()`
method takes a stub name as an argument, where the stubs are stored in
the classpath at `/stubs/<name>.json` by default. So in this example
the stub defined at `/stubs/resource.json` is loaded into the mock
server, so if the `RestTemplate` is asked to visit
`http://example.org/` it will get the responses as declared there. The
JSON format is the normal WireMock format which you can read about in
the WireMock website.
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.
=== Spring Cloud Contract Verifier

View File

@@ -1,3 +1,6 @@
:core_path: ../../../..
:doc_samples: {core_path}/samples/wiremock-jetty
Modules giving you the possibility to use
http://wiremock.org[WireMock] with different servers by using the
"ambient" server embedded in a Spring Boot application. Check out the
@@ -14,24 +17,8 @@ part of your test. Here's a simple example:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock
public class WiremockImportApplicationTests {
// A service that calls out over HTTP to localhost:8080
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
// Using the WireMock APIs in the normal way:
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}
include::{doc_samples}/src/test/java/com/example/WiremockForDocsTests.java[tags=wiremock_test1]
include::{doc_samples}/src/test/java/com/example/WiremockForDocsTests.java[tags=wiremock_test2]
----
To start the stub server on a different port use `@AutoConfigureWireMock(port=9999)` (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 `@AutoConfigureWireMock` adds a bean of type `WiremockConfiguration` 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.
@@ -42,34 +29,20 @@ class to obtain an `Options` instance:
[source,java,indent=0]
----
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options());
include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.java[tags=wiremock_test1]
include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.java[tags=wiremock_test2]
----
The use `@ClassRule` means that the server will shut down after all the methods in this class.
== WireMock and Spring MVC Mocks
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a Spring `MockRestServiceServer`. Here's an example:
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a
Spring `MockRestServiceServer`. Here's an example:
[source,java,indent=0]
----
@Autowired
private RestTemplate restTemplate;
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
MockRestServiceServer server = WireMockExpectations.with(this.restTemplate)
.baseUrl("http://example.org")
.expect("resource");
assertThat(this.service.go()).isEqualTo("Hello World");
server.verify();
}
include::{doc_samples}/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java[tags=wiremock_test]
----
The `baseUrl` is prepended to all mock calls, and the `expect()`

View File

@@ -52,4 +52,12 @@ class Service {
public String go() {
return this.restTemplate.getForEntity(this.base + "/resource", String.class).getBody();
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
}

View File

@@ -0,0 +1,56 @@
package com.example;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.cloud.contract.wiremock.WireMockSpring;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("classrule")
@DirtiesContext
//tag::wiremock_test1[]
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock
public class WiremockForDocsClassRuleTests {
// Start WireMock on some dynamic port
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().dynamicPort());
//end::wiremock_test1[]
@Before
public void setup() {
this.service.setBase("http://localhost:" + wiremock.port());
}
//tag::wiremock_test2[]
// A service that calls out over HTTP to localhost:${wiremock.port}
@Autowired
private Service service;
// 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!");
}
}
//end::wiremock_test2[]

View File

@@ -0,0 +1,39 @@
package com.example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.WireMockExpectations;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
//tag::wiremock_test[]
@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 from default /resources/stubs location
MockRestServiceServer server = WireMockExpectations.with(this.restTemplate)
.baseUrl("http://example.org")
.expect("resource");
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World");
server.verify();
}
}
//end::wiremock_test[]

View File

@@ -0,0 +1,51 @@
package com.example;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.core.env.Environment;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("docs")
@DirtiesContext
//tag::wiremock_test1[]
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock(port = 0)
public class WiremockForDocsTests {
//end::wiremock_test1[]
@Autowired Environment environment;
@Before
public void setup() {
service.setBase("http://localhost:" + this.environment.getProperty("wiremock.server.port"));
}
//tag::wiremock_test2[]
// A service that calls out over HTTP
@Autowired private Service service;
// 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!");
}
}
//end::wiremock_test2[]

View File

@@ -0,0 +1 @@
app.baseUrl: http://localhost:9090

View File

@@ -0,0 +1 @@
app.baseUrl: http://localhost:8080

View File

@@ -0,0 +1,4 @@
=== Spring Cloud Contract Specification
This module contains the specifications describing contracts. Currently we have only
one way for contract description which is the Groovy DSL.

View File

@@ -0,0 +1,80 @@
package org.springframework.cloud.contract.spec.internal
import org.springframework.cloud.contract.spec.Contract
import spock.lang.Specification
/**
* @author Marcin Grzejszczak
*/
class ContractSpec extends Specification {
def 'should work for http'() {
when:
Contract.make {
request {
url('/foo')
method('PUT')
headers {
header([
foo: 'bar'
])
}
body([
foo: 'bar'
])
}
response {
headers {
header([
foo2: 'bar'
])
}
body([
foo2: 'bar'
])
}
}
then:
noExceptionThrown()
}
def 'should work for messaging'() {
when:
Contract.make {
input {
messageFrom('input')
messageBody([
foo: 'bar'
])
messageHeaders {
header([
foo: 'bar'
])
}
}
outputMessage {
sentTo('output')
body([
foo2: 'bar'
])
headers {
header([
foo2: 'bar'
])
}
}
}
then:
noExceptionThrown()
}
def 'should generate a value if only regex is passed for client'() {
given:
Request request = new Request()
DslProperty property
when:
request.with {
property = value(consumer(regex("[0-9]{5}")))
}
then:
(property.serverValue as String).matches(/[0-9]{5}/)
}
}