Merge branch 'master' into features/docs-tuning

This commit is contained in:
Marcin Grzejszczak
2017-04-25 07:42:33 +02:00
committed by GitHub
46 changed files with 645 additions and 115 deletions

View File

@@ -94,6 +94,49 @@ include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.j
The use `@ClassRule` means that the server will shut down after all the methods in this class.
== Relaxed SSL Validation for Rest Template
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).
To make this work with minimum fuss you need to be using the Spring Boot `RestTemplateBuilder` in your app,
e.g.
[source,java,indent=0]
----
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
----
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
`@AutoConfigureWireMock` annotation (or the stub runner). If you are using the JUnit `@Rule` approach you need
to add the `@AutoConfigureHttpClient` annotation as well:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().httpsPort(6443));
...
}
----
If you are using `spring-boot-starter-test` then you will have the Apache HTTP client on the classpath and it will
be selected by the `RestTemplateBuilder` and configured to ignore SSL errors. If you are using the default `java.net`
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.
== WireMock and Spring MVC Mocks
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a

12
pom.xml
View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>1.3.1.BUILD-SNAPSHOT</version>
<version>1.3.2.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
@@ -27,12 +27,12 @@
<camel.version>2.17.0</camel.version>
<spring-boot.version>1.5.3.BUILD-SNAPSHOT</spring-boot.version>
<checkstyle.version>2.17</checkstyle.version>
<spring-cloud-build.version>1.3.1.BUILD-SNAPSHOT</spring-cloud-build.version>
<spring-cloud-zookeeper.version>1.1.0.BUILD-SNAPSHOT</spring-cloud-zookeeper.version>
<spring-cloud-build.version>1.3.2.BUILD-SNAPSHOT</spring-cloud-build.version>
<spring-cloud-zookeeper.version>1.1.1.BUILD-SNAPSHOT</spring-cloud-zookeeper.version>
<spring-cloud-stream.version>Chelsea.BUILD-SNAPSHOT</spring-cloud-stream.version>
<spring-cloud-netflix.version>1.3.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-consul.version>1.2.0.BUILD-SNAPSHOT</spring-cloud-consul.version>
<spring-cloud-commons.version>1.2.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-netflix.version>1.3.1.BUILD-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-consul.version>1.2.1.BUILD-SNAPSHOT</spring-cloud-consul.version>
<spring-cloud-commons.version>1.2.1.BUILD-SNAPSHOT</spring-cloud-commons.version>
</properties>
<modules>

View File

@@ -5,6 +5,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@@ -20,9 +21,6 @@ import com.example.loan.model.Response;
@Service
public class LoanApplicationService {
private static final String FRAUD_SERVICE_JSON_VERSION_1 =
"application/vnd.fraud.v1+json";
private final RestTemplate restTemplate;
private int port = 6565;
@@ -45,7 +43,7 @@ public class LoanApplicationService {
private FraudServiceResponse sendRequestToFraudDetectionService(
FraudServiceRequest request) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1);
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
// tag::client_call_server[]
ResponseEntity<FraudServiceResponse> response =
@@ -70,7 +68,7 @@ public class LoanApplicationService {
public int countAllFrauds() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1);
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<Response> response =
restTemplate.exchange("http://localhost:" + port + "/frauds", HttpMethod.GET,
new HttpEntity<>(httpHeaders),
@@ -80,7 +78,7 @@ public class LoanApplicationService {
public int countDrunks() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1);
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<Response> response =
restTemplate.exchange("http://localhost:" + port + "/drunks", HttpMethod.GET,
new HttpEntity<>(httpHeaders),

View File

@@ -117,7 +117,7 @@
<pluginExecutionFilter>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<versionRange>[1.1.0.BUILD-SNAPSHOT,)</versionRange>
<versionRange>[1.1.1.BUILD-SNAPSHOT,)</versionRange>
<goals>
<goal>convert</goal>
<goal>generateTests</goal>

View File

@@ -15,17 +15,12 @@ import static org.springframework.web.bind.annotation.RequestMethod.PUT;
@RestController
public class FraudDetectionController {
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
private static final String NO_REASON = null;
private static final String AMOUNT_TOO_HIGH = "Amount too high";
private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000");
// tag::server_api[]
@RequestMapping(
value = "/fraudcheck",
method = PUT,
consumes = FRAUD_SERVICE_JSON_VERSION_1,
produces = FRAUD_SERVICE_JSON_VERSION_1)
@RequestMapping(value = "/fraudcheck", method = PUT)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
// end::server_api[]
// tag::new_impl[]

View File

@@ -10,17 +10,13 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
class FraudNameController {
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
private final FraudVerifier fraudVerifier;
FraudNameController(FraudVerifier fraudVerifier) {
this.fraudVerifier = fraudVerifier;
}
@PutMapping(
value = "/frauds/name",
produces = FRAUD_SERVICE_JSON_VERSION_1)
@PutMapping(value = "/frauds/name")
public NameResponse checkByName(@RequestBody NameRequest request) {
boolean fraud = this.fraudVerifier.isFraudByName(request.getName());
if (fraud) {

View File

@@ -6,24 +6,18 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class FraudStatsController {
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
private final StatsProvider statsProvider;
public FraudStatsController(StatsProvider statsProvider) {
this.statsProvider = statsProvider;
}
@GetMapping(
value = "/frauds",
produces = FRAUD_SERVICE_JSON_VERSION_1)
@GetMapping(value = "/frauds")
public Response countAllFrauds() {
return new Response(this.statsProvider.count(FraudType.ALL));
}
@GetMapping(
value = "/drunks",
produces = FRAUD_SERVICE_JSON_VERSION_1)
@GetMapping(value = "/drunks")
public Response countAllDrunks() {
return new Response(this.statsProvider.count(FraudType.DRUNKS));
}

View File

@@ -9,7 +9,7 @@ org.springframework.cloud.contract.spec.Contract.make {
loanAmount: 99999
])
headers { // (5)
contentType('application/vnd.fraud.v1+json')
contentType('application/json')
}
}
response { // (6)
@@ -19,7 +19,7 @@ org.springframework.cloud.contract.spec.Contract.make {
rejectionReason: "Amount too high"
])
headers { // (9)
contentType('application/vnd.fraud.v1+json')
contentType('application/json')
}
}
}
@@ -33,12 +33,12 @@ From the Consumer perspective, when shooting a request in the integration test:
(4) - with the JSON body that
* has a field `clientId` that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
(9) - with header `Content-Type` equal to `application/json`
From the Producer perspective, in the autogenerated producer-side test:
@@ -48,10 +48,10 @@ From the Producer perspective, in the autogenerated producer-side test:
(4) - with the JSON body that
* has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/vnd.fraud.v1+json.*`
(9) - with header `Content-Type` matching `application/json.*`
*/

View File

@@ -12,7 +12,7 @@ org.springframework.cloud.contract.spec.Contract.make {
"""
)
headers {
contentType("application/vnd.fraud.v1+json")
contentType("application/json")
}
}
@@ -23,7 +23,7 @@ org.springframework.cloud.contract.spec.Contract.make {
rejectionReason: $(consumer(null), producer(execute('assertThatRejectionReasonIsNull($it)')))
)
headers {
contentType("application/vnd.fraud.v1+json")
contentType("application/json")
}
}

View File

@@ -15,7 +15,7 @@ import org.springframework.cloud.contract.spec.Contract
count: 200
])
headers {
contentType("application/vnd.fraud.v1+json")
contentType("application/json")
}
}
},
@@ -30,7 +30,7 @@ import org.springframework.cloud.contract.spec.Contract
count: 100
])
headers {
contentType("application/vnd.fraud.v1+json")
contentType("application/json")
}
}
}

View File

@@ -10,7 +10,7 @@ org.springframework.cloud.contract.spec.Contract.make {
name: "fraud"
])
headers {
contentType("application/vnd.fraud.v1+json")
contentType("application/json")
}
}
response {

View File

@@ -8,7 +8,7 @@ org.springframework.cloud.contract.spec.Contract.make {
name: $(anyAlphaUnicode())
])
headers {
contentType("application/vnd.fraud.v1+json")
contentType("application/json")
}
}
response {

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -156,7 +156,7 @@
<pluginExecutionFilter>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<versionRange>[1.1.0.BUILD-SNAPSHOT,)</versionRange>
<versionRange>[1.1.1.BUILD-SNAPSHOT,)</versionRange>
<goals>
<goal>convert</goal>
<goal>generateTests</goal>

View File

@@ -125,7 +125,7 @@
<pluginExecutionFilter>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<versionRange>[1.1.0.BUILD-SNAPSHOT,)</versionRange>
<versionRange>[1.1.1.BUILD-SNAPSHOT,)</versionRange>
<goals>
<goal>convert</goal>
<goal>generateTests</goal>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-samples</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -3,6 +3,7 @@ package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -13,8 +14,8 @@ import org.springframework.web.client.RestTemplate;
public class WiremockTestsApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public static void main(String[] args) {

View File

@@ -1,26 +1,28 @@
package com.example;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
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.cloud.contract.wiremock.AutoConfigureHttpClient;
import org.springframework.cloud.contract.wiremock.WireMockSpring;
import org.springframework.test.annotation.DirtiesContext;
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;
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.cloud.contract.wiremock.WireMockSpring;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:8443")
@DirtiesContext
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {
@ClassRule
@@ -32,8 +34,8 @@ public class WiremockHttpsServerApplicationTests {
@Test
public void contextLoads() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}

View File

@@ -1,14 +1,17 @@
package com.example;
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;
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.apache.http.MalformedChunkCodingException;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
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;
@@ -16,7 +19,12 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring;
import org.springframework.test.annotation.DirtiesContext;
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.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6061", webEnvironment=WebEnvironment.NONE)
@@ -26,14 +34,41 @@ public class WiremockServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6061));
@Rule
public ExpectedException expected = ExpectedException.none();
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
public void hello() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");
}
@Test
public void randomData() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE)));
expected.expectCause(instanceOf(ClientProtocolException.class));
assertThat(this.service.go()).isEqualTo("Oops!");
}
@Test
public void emptyResponse() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
expected.expectCause(instanceOf(NoHttpResponseException.class));
assertThat(this.service.go()).isEqualTo("Oops!");
}
@Test
public void malformed() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK)));
expected.expectCause(instanceOf(MalformedChunkCodingException.class));
assertThat(this.service.go()).isEqualTo("Oops!");
}
}

View File

@@ -3,6 +3,7 @@ package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -13,8 +14,8 @@ import org.springframework.web.client.RestTemplate;
public class WiremockTestsApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public static void main(String[] args) {

View File

@@ -11,6 +11,7 @@ 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.cloud.contract.wiremock.AutoConfigureHttpClient;
import org.springframework.cloud.contract.wiremock.WireMockSpring;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
@@ -21,6 +22,7 @@ import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@DirtiesContext
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {
@ClassRule

View File

@@ -1,14 +1,18 @@
package com.example;
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;
import java.io.IOException;
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
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;
@@ -16,7 +20,12 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring;
import org.springframework.test.annotation.DirtiesContext;
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.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
@RunWith(SpringRunner.class)
@SpringBootTest(properties="app.baseUrl=http://localhost:6067", webEnvironment=WebEnvironment.NONE)
@@ -26,6 +35,9 @@ public class WiremockServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6067));
@Rule
public ExpectedException expected = ExpectedException.none();
@Autowired
private Service service;
@@ -36,4 +48,30 @@ public class WiremockServerApplicationTests {
assertThat(this.service.go()).isEqualTo("Hello World!");
}
@Test
public void randomData() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE)));
expected.expectCause(instanceOf(ClientProtocolException.class));
assertThat(this.service.go()).isEqualTo("Oops!");
}
@Test
public void emptyResponse() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
expected.expectCause(instanceOf(NoHttpResponseException.class));
assertThat(this.service.go()).isEqualTo("Oops!");
}
@Test
public void malformed() throws Exception {
stubFor(get(urlEqualTo("/resource"))
.willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK)));
// It's a different exception type than Jetty, but it's in the right ballpark
expected.expectCause(instanceOf(IOException.class));
expected.expectMessage("chunk");
assertThat(this.service.go()).isEqualTo("Oops!");
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>1.3.1.BUILD-SNAPSHOT</version>
<version>1.3.2.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-contract-dependencies</artifactId>

View File

@@ -23,7 +23,7 @@ ext {
]
}
project.version = findProperty('verifierVersion') ?: '1.1.0.BUILD-SNAPSHOT'
project.version = findProperty('verifierVersion') ?: '1.1.1.BUILD-SNAPSHOT'
apply plugin: 'groovy'
apply from: "$rootDir/gradle/release.gradle"
apply plugin: 'eclipse'

View File

@@ -16,5 +16,5 @@
nexusUsername =
nexusPassword =
verifierVersion=1.1.0.BUILD-SNAPSHOT
verifierVersion=1.1.1.BUILD-SNAPSHOT
org.gradle.daemon=false

View File

@@ -15,5 +15,5 @@
#
wiremockVersion=2.5.1
jsonAssertVersion=0.4.8
verifierVersion=1.1.0.BUILD-SNAPSHOT
verifierVersion=1.1.1.BUILD-SNAPSHOT

View File

@@ -15,4 +15,4 @@
#
wiremockVersion=2.5.1
jsonAssertVersion=0.4.8
verifierVersion=1.1.0.BUILD-SNAPSHOT
verifierVersion=1.1.1.BUILD-SNAPSHOT

View File

@@ -15,4 +15,4 @@
#
wiremockVersion=2.5.1
jsonAssertVersion=0.4.8
verifierVersion=1.1.0.BUILD-SNAPSHOT
verifierVersion=1.1.1.BUILD-SNAPSHOT

View File

@@ -15,5 +15,5 @@
#
wiremockVersion=2.5.1
jsonAssertVersion=0.4.8
verifierVersion=1.1.0.BUILD-SNAPSHOT
verifierVersion=1.1.1.BUILD-SNAPSHOT

View File

@@ -30,7 +30,7 @@
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.1.BUILD-SNAPSHOT</version>
<configuration>
<contractsPath>com/example/server</contractsPath>
<contractDependency>

View File

@@ -26,7 +26,7 @@
<version>0.1</version>
<properties>
<spring.cloud.contract.version>1.1.0.BUILD-SNAPSHOT</spring.cloud.contract.version>
<spring.cloud.contract.version>1.1.1.BUILD-SNAPSHOT</spring.cloud.contract.version>
</properties>
<build>

View File

@@ -27,7 +27,6 @@ import groovy.transform.PackageScope
*
* @since 1.0.0
*/
@PackageScope
@CompileStatic
class BlockBuilder {
@@ -43,21 +42,33 @@ class BlockBuilder {
builder = new StringBuilder()
}
/**
* Adds indents to start a new block
*/
BlockBuilder startBlock() {
indents++
return this
}
/**
* Ends block by removing indents
*/
BlockBuilder endBlock() {
indents--
return this
}
/**
* Creates a block and adds indents
*/
BlockBuilder indent() {
startBlock().startBlock()
return this
}
/**
* Removes indents and closes the block
*/
BlockBuilder unindent() {
endBlock().endBlock()
return this
@@ -81,6 +92,7 @@ class BlockBuilder {
}
}
@PackageScope
BlockBuilder addBlock(MethodBuilder methodBuilder) {
startBlock()
methodBuilder.appendTo(this)
@@ -89,6 +101,11 @@ class BlockBuilder {
return this
}
/**
* Adds the given text at the end of the line
*
* @return updated BlockBuilder
*/
BlockBuilder addAtTheEnd(String toAdd) {
String lastChar = builder.charAt(builder.length() - 1) as String
String secondLastChar = builder.length() >= 2 ? builder.charAt(builder.length() - 2) as String : ""
@@ -114,6 +131,12 @@ class BlockBuilder {
return character == "{" || character == toAdd
}
/**
* Updates the current text with the provided one
*
* @param contents - text to replace the current content with
* @return updated Block Builder
*/
BlockBuilder updateContents(String contents) {
this.builder.replace(0, this.builder.length(), contents)
return this

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.context.annotation.Import;
/**
* Annotation for test classes that want to install a RestTemplateCustomizer that sets up
* a Spring Boot app to ignore SSL errors. Use only in tests!
*
* @author Dave Syer
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(WireMockRestTemplateConfiguration.class)
@PropertyMapping("wiremock.server")
public @interface AutoConfigureHttpClient {
}

View File

@@ -39,6 +39,7 @@ import org.springframework.context.annotation.Import;
@Documented
@Import(WireMockConfiguration.class)
@PropertyMapping("wiremock.server")
@AutoConfigureHttpClient
public @interface AutoConfigureWireMock {
int port() default 8080;

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock;
import java.io.IOException;
import java.nio.channels.ByteChannel;
import javax.servlet.http.HttpServletResponse;
import com.github.tomakehurst.wiremock.core.FaultInjector;
import com.google.common.base.Charsets;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.BufferUtil;
import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked;
import static com.github.tomakehurst.wiremock.jetty9.JettyUtils.unwrapResponse;
/**
* @author Dave Syer
*
*/
public class JettyFaultInjector implements FaultInjector {
private static final byte[] GARBAGE = "lskdu018973t09sylgasjkfg1][]'./.sdlv"
.getBytes(Charsets.UTF_8);
private final Response response;
private final ByteChannel socket;
public JettyFaultInjector(HttpServletResponse response) {
this.response = unwrapResponse(response);
this.socket = socket();
}
@Override
public void emptyResponseAndCloseConnection() {
try {
this.socket.close();
}
catch (IOException e) {
throwUnchecked(e);
}
}
@Override
public void malformedResponseChunk() {
try {
this.response.setStatus(200);
this.response.flushBuffer();
this.socket.write(BufferUtil.toBuffer(GARBAGE));
this.socket.close();
}
catch (IOException e) {
throwUnchecked(e);
}
}
@Override
public void randomDataAndCloseConnection() {
try {
this.socket.write(BufferUtil.toBuffer(GARBAGE));
this.socket.close();
}
catch (IOException e) {
throwUnchecked(e);
}
}
private ByteChannel socket() {
HttpChannel httpChannel = this.response.getHttpOutput().getHttpChannel();
ChannelEndPoint ep = (ChannelEndPoint) httpChannel.getEndPoint();
return ep.getChannel();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.github.tomakehurst.wiremock.core.FaultInjector;
import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory;
/**
* @author Dave Syer
*
*/
public class JettyFaultInjectorFactory implements FaultInjectorFactory {
@Override
public FaultInjector buildFaultInjector(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
return new JettyFaultInjector(httpServletResponse);
}
}

View File

@@ -18,11 +18,27 @@ package org.springframework.cloud.contract.wiremock;
import javax.servlet.ServletContext;
import io.undertow.Undertow.Builder;
import com.github.tomakehurst.wiremock.common.HttpsSettings;
import com.github.tomakehurst.wiremock.common.Notifier;
import com.github.tomakehurst.wiremock.core.Options;
import com.github.tomakehurst.wiremock.core.WireMockApp;
import com.github.tomakehurst.wiremock.http.AdminRequestHandler;
import com.github.tomakehurst.wiremock.http.HttpServer;
import com.github.tomakehurst.wiremock.http.HttpServerFactory;
import com.github.tomakehurst.wiremock.http.RequestHandler;
import com.github.tomakehurst.wiremock.http.StubRequestHandler;
import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory;
import com.github.tomakehurst.wiremock.servlet.NoFaultInjectorFactory;
import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet;
import org.apache.catalina.connector.Connector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -64,19 +80,6 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;
import com.github.tomakehurst.wiremock.common.HttpsSettings;
import com.github.tomakehurst.wiremock.common.Notifier;
import com.github.tomakehurst.wiremock.core.Options;
import com.github.tomakehurst.wiremock.core.WireMockApp;
import com.github.tomakehurst.wiremock.http.AdminRequestHandler;
import com.github.tomakehurst.wiremock.http.HttpServer;
import com.github.tomakehurst.wiremock.http.HttpServerFactory;
import com.github.tomakehurst.wiremock.http.RequestHandler;
import com.github.tomakehurst.wiremock.http.StubRequestHandler;
import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet;
import io.undertow.Undertow.Builder;
/**
* @author Dave Syer
*
@@ -258,6 +261,8 @@ class WiremockServerConfiguration {
@Autowired
private StubRequestHandler stubRequestHandler;
@Autowired
private FaultInjectorFactory faultInjectorFactory;
@Autowired
private Options options;
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@@ -265,6 +270,10 @@ class WiremockServerConfiguration {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.addInitParameter(RequestHandler.HANDLER_CLASS_KEY,
StubRequestHandler.class.getName());
if (WiremockServerConfiguration.this.faultInjectorFactory != null) {
reg.addInitParameter(FaultInjectorFactory.INJECTOR_CLASS_KEY,
FaultInjectorFactory.class.getName());
}
reg.setServlet(new WireMockHandlerDispatchingServlet());
reg.setName("stub");
reg.addUrlMappings("/");
@@ -293,6 +302,10 @@ class WiremockServerConfiguration {
WiremockServerConfiguration.this.stubRequestHandler);
servletContext.setAttribute(Notifier.KEY,
WiremockServerConfiguration.this.options.notifier());
if (WiremockServerConfiguration.this.faultInjectorFactory != null) {
servletContext.setAttribute(FaultInjectorFactory.class.getName(),
WiremockServerConfiguration.this.faultInjectorFactory);
}
}
};
}
@@ -367,6 +380,11 @@ class ContainerConfiguration {
return tomcat;
}
@Bean
public FaultInjectorFactory faultInjectorFactory() {
return new TomcatFaultInjectorFactory();
}
@EventListener
public void serverUp(EmbeddedServletContainerInitializedEvent event) {
if (this.connector != null) {
@@ -417,6 +435,11 @@ class ContainerConfiguration {
return undertow;
}
@Bean
public FaultInjectorFactory faultInjectorFactory() {
return new NoFaultInjectorFactory();
}
@EventListener
public void serverUp(EmbeddedServletContainerInitializedEvent event) {
if (this.port != null) {
@@ -457,6 +480,11 @@ class ContainerConfiguration {
return jetty;
}
@Bean
public JettyFaultInjectorFactory faultInjectorFactory() {
return new JettyFaultInjectorFactory();
}
private org.eclipse.jetty.server.Connector createStandardConnector(
Server server) {
ServerConnector connector = new ServerConnector(server, -1, -1);

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import javax.servlet.http.HttpServletResponse;
import com.github.tomakehurst.wiremock.common.Exceptions;
import com.github.tomakehurst.wiremock.core.FaultInjector;
import com.google.common.base.Charsets;
import org.apache.coyote.Response;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.springframework.util.ReflectionUtils;
import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked;
/**
* @author Dave Syer
*
*/
public class TomcatFaultInjector implements FaultInjector {
private static final byte[] GARBAGE = "lskdu018973t09sylgasjkfg1][]'./.sdlv"
.getBytes(Charsets.UTF_8);
private final Response response;
private SocketWrapperBase<?> socket;
public TomcatFaultInjector(HttpServletResponse response) {
this.response = ((org.apache.catalina.connector.Response) getField(response,
"response")).getCoyoteResponse();
this.socket = (SocketWrapperBase<?>) getField(
getField(this.response, "outputBuffer"), "socketWrapper");
}
private Object getField(Object target, String string) {
Field field = ReflectionUtils.findField(target.getClass(), string);
ReflectionUtils.makeAccessible(field);
return ReflectionUtils.getField(field, target);
}
@Override
public void emptyResponseAndCloseConnection() {
try {
this.socket.close();
}
catch (IOException e) {
Exceptions.throwUnchecked(e);
}
}
@Override
public void malformedResponseChunk() {
try {
this.response.sendHeaders();
this.response.doWrite(ByteBuffer.wrap(GARBAGE));
this.socket.flush(true);
this.socket.close();
}
catch (IOException e) {
throwUnchecked(e);
}
}
@Override
public void randomDataAndCloseConnection() {
try {
this.socket.write(true, GARBAGE, 0, GARBAGE.length);
this.socket.flush(true);
this.socket.close();
}
catch (IOException e) {
throwUnchecked(e);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.github.tomakehurst.wiremock.core.FaultInjector;
import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory;
/**
* @author Dave Syer
*
*/
public class TomcatFaultInjectorFactory implements FaultInjectorFactory {
@Override
public FaultInjector buildFaultInjector(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
return new TomcatFaultInjector(httpServletResponse);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* @author Dave Syer
*
*/
@Configuration
public class WireMockRestTemplateConfiguration {
@Bean
@ConditionalOnClass(SSLContextBuilder.class)
public RestTemplateCustomizer restTemplateCustomizer() {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
HttpComponentsClientHttpRequestFactory factory = (HttpComponentsClientHttpRequestFactory) restTemplate
.getRequestFactory();
factory.setHttpClient(createSslHttpClient());
}
private HttpClient createSslHttpClient() {
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder().loadTrustMaterial(null,
TrustSelfSignedStrategy.INSTANCE).build(),
NoopHostnameVerifier.INSTANCE);
return HttpClients.custom().setSSLSocketFactory(socketFactory)
.build();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create SSL HttpClient",
ex);
}
}
};
}
}

View File

@@ -18,13 +18,14 @@ package org.springframework.cloud.contract.wiremock;
import javax.net.ssl.HttpsURLConnection;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContexts;
import org.junit.Assert;
import org.springframework.util.ClassUtils;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.springframework.util.ClassUtils;
/**
* Convenience factory class for a {@link WireMockConfiguration} that knows how to use

View File

@@ -4,4 +4,4 @@ org.springframework.cloud.contract.wiremock.WireMockApplicationListener
# RestDocs Auto Configuration
org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs=\
org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocsConfiguration
org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocsConfiguration

View File

@@ -1,21 +1,22 @@
package org.springframework.cloud.contract.wiremock;
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;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
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.test.annotation.DirtiesContext;
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.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(classes=WiremockTestsApplication.class, properties="app.baseUrl=http://localhost:8080", webEnvironment=WebEnvironment.NONE)
@@ -29,7 +30,7 @@ public class WiremockServerApplicationTests {
private Service service;
@Test
public void contextLoads() throws Exception {
public void hello() throws Exception {
stubFor(get(urlEqualTo("/test"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.go()).isEqualTo("Hello World!");