web dependency is optional, StubRunner listener conditional
without this change we always include `spring-boot-starter-web` to Stub Runner. With this change the dependency is optional. Thanks to this reactive applications will be able to work fine with contract on the classpath. without this change we always run Stub Runner test execution listener, even if the test class is not annotated. With this change we run the listener only, if the test class was annotated with `@AutoConfigureStubRunner`. Also we add information about the `java.net.SocketException: Unexpected end of file from server` exception to the docs and how to override that issue. Fixes gh-798, gh-809, gh-799
This commit is contained in:
24
README.adoc
24
README.adoc
@@ -5,6 +5,7 @@ image::https://badges.gitter.im/Join%20Chat.svg[Gitter, link="https://gitter.im/
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/{branch}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"]
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-contract.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-contract"]
|
||||
:introduction_url: ../../../..
|
||||
:verifier_core_path: {introduction_url}/spring-cloud-contract-verifier
|
||||
|
||||
== Spring Cloud Contract
|
||||
|
||||
@@ -331,14 +332,33 @@ The following example shows a Camel messaging contract expressed in Groovy DSL:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
Unresolved directive in verifier_introduction.adoc - include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl]
|
||||
def contractDsl = Contract.make {
|
||||
label 'some_label'
|
||||
input {
|
||||
messageFrom('jms:delete')
|
||||
messageBody([
|
||||
bookName: 'foo'
|
||||
])
|
||||
messageHeaders {
|
||||
header('sample', 'header')
|
||||
}
|
||||
assertThat('bookWasDeleted()')
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The following example shows the same contract expressed in YAML:
|
||||
|
||||
[source,yml,indent=0]
|
||||
----
|
||||
Unresolved directive in verifier_introduction.adoc - include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0]
|
||||
label: some_label
|
||||
input:
|
||||
messageFrom: jms:delete
|
||||
messageBody:
|
||||
bookName: 'foo'
|
||||
messageHeaders:
|
||||
sample: header
|
||||
assertThat: bookWasDeleted()
|
||||
----
|
||||
|
||||
Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file,
|
||||
|
||||
@@ -3,6 +3,7 @@ image::https://badges.gitter.im/Join%20Chat.svg[Gitter, link="https://gitter.im/
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/{branch}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"]
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-contract.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-contract"]
|
||||
:introduction_url: ../../../..
|
||||
:verifier_core_path: {introduction_url}/spring-cloud-contract-verifier
|
||||
|
||||
== Spring Cloud Contract
|
||||
|
||||
|
||||
@@ -936,4 +936,40 @@ started will be attached.
|
||||
|
||||
Yes! With version 1.2.0 we've added such a possibility. It's enough to call `file(...)` method in the
|
||||
DSL and provide a path relative to where the contract lays.
|
||||
If you're using YAML just use the `bodyFromFile` property.
|
||||
If you're using YAML just use the `bodyFromFile` property.
|
||||
|
||||
==== Why do I sometimes get `SocketException`
|
||||
|
||||
When using an HTTP client (e.g. `RestTemplate`) and running several tests that share a Spring context, you might sometimes get the following exception:
|
||||
|
||||
```
|
||||
java.net.SocketException: Unexpected end of file from server
|
||||
```
|
||||
|
||||
Tom Akehurst, the creator of WireMock did the following analysis of this issue.
|
||||
|
||||
> I looked at tcpdump while running the failing test. `HttpUrlConnection` is doing something weird - it's creating a connection in a previous test case, which works fine, then the usual `fin` -> `fin ack` etc. ending handshake happens. But it seems it isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet. Calling the `/__admin` endpoint just happened to remove the dead connection from the pool. This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false");
|
||||
|
||||
There are the ways to solve this problem.
|
||||
|
||||
First, just use a different HTTP client for `RestTemplate`. Example for using Apache HTTP client:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{standalone_restdocs_path}/http-client/pom.xml[tags=httpclient,indent=0]
|
||||
----
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{standalone_restdocs_path}/http-client/src/main/java/com/example/loan/LoanApplicationService.java[tags=custom_request_factory,indent=0]
|
||||
----
|
||||
|
||||
Second option is to set the system property. You can set it either in code or pass it to your tests via a plugin.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
static {
|
||||
System.setProperty("http.keepAlive", "false");
|
||||
}
|
||||
----
|
||||
@@ -1,4 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2018 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -53,6 +70,13 @@
|
||||
<artifactId>spring-cloud-contract-stub-runner</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- tag::httpclient[] -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- end::httpclient[] -->
|
||||
<dependency>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>http-server-restdocs</artifactId>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
package com.example.loan;
|
||||
/*
|
||||
* Copyright 2013-2018 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
package com.example.loan;
|
||||
|
||||
import com.example.loan.model.FraudCheckStatus;
|
||||
import com.example.loan.model.FraudServiceRequest;
|
||||
@@ -15,6 +24,15 @@ import com.example.loan.model.LoanApplication;
|
||||
import com.example.loan.model.LoanApplicationResult;
|
||||
import com.example.loan.model.LoanApplicationStatus;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Service
|
||||
@ConfigurationProperties("service")
|
||||
public class LoanApplicationService {
|
||||
@@ -28,6 +46,10 @@ public class LoanApplicationService {
|
||||
|
||||
public LoanApplicationService() {
|
||||
this.restTemplate = new RestTemplate();
|
||||
// tag::custom_request_factory[]
|
||||
this.restTemplate
|
||||
.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
|
||||
// end::custom_request_factory[]
|
||||
}
|
||||
|
||||
public LoanApplicationResult loanApplication(LoanApplication loanApplication) {
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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 com.example.loan;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -8,6 +25,7 @@ import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2018 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -20,6 +37,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.stubrunner.provider.wiremock;
|
||||
|
||||
import java.util.List;
|
||||
@@ -7,12 +24,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.HttpServerStub;
|
||||
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
|
||||
import org.springframework.cloud.contract.wiremock.WireMockUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Stops the {@link HttpServerStub} after each test class
|
||||
@@ -20,46 +38,55 @@ import org.springframework.web.client.RestTemplate;
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.6
|
||||
*/
|
||||
public final class StubRunnerWireMockTestExecutionListener extends AbstractTestExecutionListener {
|
||||
public final class StubRunnerWireMockTestExecutionListener
|
||||
extends AbstractTestExecutionListener {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StubRunnerWireMockTestExecutionListener.class);
|
||||
private static final Log log = LogFactory
|
||||
.getLog(StubRunnerWireMockTestExecutionListener.class);
|
||||
|
||||
private static Map<ApplicationContext, Map<WireMockHttpServerStub, PortAndMappings>> STUBS = new ConcurrentHashMap<>();
|
||||
|
||||
@Override public void beforeTestClass(TestContext testContext) {
|
||||
@Override
|
||||
public void beforeTestClass(TestContext testContext) {
|
||||
if (testContext.getTestClass().getAnnotationsByType(AutoConfigureStubRunner.class).length == 0) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("No @AutoConfigureStubRunner annotation found on [" + testContext.getTestClass() + "]. Skipping");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Map<WireMockHttpServerStub, PortAndMappings> stubs = STUBS
|
||||
.get(testContext.getApplicationContext());
|
||||
if (stubs != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found a matching application context from [" + testContext.getTestClass().getName() + "]");
|
||||
log.debug("Found a matching application context from ["
|
||||
+ testContext.getTestClass().getName() + "]");
|
||||
}
|
||||
for (Map.Entry<WireMockHttpServerStub, PortAndMappings> entry : stubs.entrySet()) {
|
||||
for (Map.Entry<WireMockHttpServerStub, PortAndMappings> entry : stubs
|
||||
.entrySet()) {
|
||||
while (entry.getKey().isRunning()) {
|
||||
entry.getKey().stop();
|
||||
}
|
||||
List<StubMapping> mappings = entry.getValue().mappings;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stopped a running WireMock instance at "
|
||||
+ "port [" + entry.getValue().port + "] with stub mappings "
|
||||
+ "size [" + mappings.size() + "]. Restarting the stub.");
|
||||
log.debug("Stopped a running WireMock instance at " + "port ["
|
||||
+ entry.getValue().port + "] with stub mappings size ["
|
||||
+ mappings.size() + "]. Restarting the stub.");
|
||||
}
|
||||
entry.getKey().start(entry.getValue().port);
|
||||
entry.getKey().registerDescriptors(mappings);
|
||||
/*
|
||||
Thanks to Tom Akehurst:
|
||||
I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a
|
||||
previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it
|
||||
isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet.
|
||||
Calling the admin endpoint just happened to remove the dead connection from the pool.
|
||||
This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false");
|
||||
*/
|
||||
Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + entry.getValue().port + "/__admin/mappings", String.class)
|
||||
.getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible");
|
||||
WireMockUtils.getMappingsEndpoint(entry.getValue().port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void afterTestClass(TestContext testContext) {
|
||||
@Override
|
||||
public void afterTestClass(TestContext testContext) {
|
||||
if (testContext.getTestClass().getAnnotationsByType(AutoConfigureStubRunner.class).length == 0) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("No @AutoConfigureStubRunner annotation found on [" + testContext.getTestClass() + "]. Skipping");
|
||||
}
|
||||
return;
|
||||
}
|
||||
STUBS.put(testContext.getApplicationContext(), WireMockHttpServerStub.SERVERS);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stopping servers " + WireMockHttpServerStub.SERVERS);
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.stubrunner.provider.wiremock;
|
||||
|
||||
import java.io.File;
|
||||
@@ -20,6 +37,8 @@ import com.github.tomakehurst.wiremock.extension.Extension;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import wiremock.com.github.jknack.handlebars.Helper;
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.HttpServerStub;
|
||||
import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsEscapeHelper;
|
||||
import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsJsonPathHelper;
|
||||
@@ -31,7 +50,6 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import wiremock.com.github.jknack.handlebars.Helper;
|
||||
|
||||
/**
|
||||
* Abstraction over WireMock as a HTTP Server Stub
|
||||
@@ -115,7 +133,7 @@ public class WireMockHttpServerStub implements HttpServerStub {
|
||||
log.debug("Started WireMock at port [" + port + "]");
|
||||
}
|
||||
if (!SERVERS.containsKey(this)) {
|
||||
SERVERS.put(this, new PortAndMappings(port, new ArrayList<StubMapping>()));
|
||||
SERVERS.put(this, new PortAndMappings(port, new ArrayList<>()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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
|
||||
* 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;
|
||||
@@ -20,6 +21,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
@@ -28,6 +30,7 @@ import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
|
||||
import com.github.tomakehurst.wiremock.core.Options;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -38,10 +41,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Configuration and lifecycle for a Spring Application context that wants to run a
|
||||
@@ -50,7 +51,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
* configure the properties of the wiremock server you can use the AutoConfigureWireMock
|
||||
* annotation, or add a bean of type {@link Options} (via
|
||||
* {@link WireMockSpring#options()}) to your test context.
|
||||
*
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@@ -103,7 +104,8 @@ public class WireMockConfiguration implements SmartLifecycle {
|
||||
}
|
||||
registerStubs();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("WireMock server has [" + this.server.getStubMappings().size() + "] registered");
|
||||
log.debug("WireMock server has [" + this.server.getStubMappings().size()
|
||||
+ "] stubs registered");
|
||||
}
|
||||
if (!this.beanFactory.containsBean(WIREMOCK_SERVER_BEAN_NAME)) {
|
||||
this.beanFactory.registerSingleton(WIREMOCK_SERVER_BEAN_NAME, this.server);
|
||||
@@ -130,6 +132,10 @@ public class WireMockConfiguration implements SmartLifecycle {
|
||||
}
|
||||
}
|
||||
|
||||
int port() {
|
||||
return this.server.port();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this.server.resetAll();
|
||||
}
|
||||
@@ -161,22 +167,14 @@ public class WireMockConfiguration implements SmartLifecycle {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Started WireMock at port [" + this.server.port() + "]. It has [" + this.server.getStubMappings().size() + "] mappings registered");
|
||||
}
|
||||
/*
|
||||
Thanks to Tom Akehurst:
|
||||
I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a
|
||||
previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it
|
||||
isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet.
|
||||
Calling the admin endpoint just happened to remove the dead connection from the pool.
|
||||
This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false");
|
||||
*/
|
||||
Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + this.server.port() + "/__admin/mappings", String.class)
|
||||
.getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible");
|
||||
WireMockUtils.getMappingsEndpoint(this.port());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.running) {
|
||||
this.server.stop();
|
||||
reset();
|
||||
this.server.shutdownServer();
|
||||
this.running = false;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stopped WireMock instance");
|
||||
|
||||
@@ -1,7 +1,25 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
|
||||
@@ -25,9 +43,9 @@ public final class WireMockTestExecutionListener extends AbstractTestExecutionLi
|
||||
log.debug("WireMock configuration is running [" + wireMockConfiguration.isRunning() + "]");
|
||||
}
|
||||
if (!wireMockConfiguration.isRunning()) {
|
||||
wireMockConfiguration.reset();
|
||||
wireMockConfiguration.init();
|
||||
wireMockConfiguration.start();
|
||||
WireMockUtils.getMappingsEndpoint(wireMockConfiguration.port());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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 org.apache.http.HttpHost;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class to work with WireMock.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public final class WireMockUtils {
|
||||
|
||||
private WireMockUtils() {
|
||||
throw new IllegalStateException("Don't instantiate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Thanks to Tom Akehurst: I looked at tcpdump while running the failing
|
||||
* test. HttpUrlConnection is doing something weird - it's creating a
|
||||
* connection in a previous test case, which works fine, then the usual
|
||||
* fin -> fin ack etc. etc. ending handshake happens. But it seems it
|
||||
* isn't discarded, but reused after that. Because the server thinks
|
||||
* (rightly) that the connection is closed, it just sends a RST packet.
|
||||
* Calling the admin endpoint just happened to remove the dead connection
|
||||
* from the pool. This also fixes the problem (which using the Java HTTP
|
||||
* client): System.setProperty("http.keepAlive", "false");
|
||||
**/
|
||||
public static CloseableHttpResponse getMappingsEndpoint(int port) {
|
||||
CloseableHttpClient client = HttpClientBuilder.create().build();
|
||||
try {
|
||||
CloseableHttpResponse response = client
|
||||
.execute(new HttpHost("localhost", port), new HttpGet("/__admin/mappings"));
|
||||
Assert.isTrue(response.getStatusLine().getStatusCode() == 200, "Status code must be 200");
|
||||
return response;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user