Add support for port and random port in WireMock
User can @AutoConfigureWireMock(port=0) for instance. Still missing: declarative HTTPS.
This commit is contained in:
@@ -8,7 +8,7 @@ Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer_
|
||||
|
||||
== Spring Cloud Contract
|
||||
|
||||
What you always need it confidence in pushing new features into a new application or service in a distributed system.
|
||||
What you always need is confidence in pushing new features into a new application or service in a distributed system.
|
||||
This project provides support for Consumer Driven Contracts and service schemas in Spring applications, covering a
|
||||
range of options for writing tests, publishing them as assets, asserting that a contract is kept by producers
|
||||
and consumers, for HTTP and message-based interactions.
|
||||
|
||||
@@ -1,4 +1,88 @@
|
||||
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.
|
||||
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.
|
||||
|
||||
Currently we support Jetty, Native WireMock server, Tomcat and Undertow.
|
||||
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
|
||||
@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!");
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
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]
|
||||
----
|
||||
@ClassRule
|
||||
public static WireMockClassRule wiremock = new WireMockClassRule(
|
||||
WireMockSpring.options());
|
||||
----
|
||||
|
||||
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]
|
||||
----
|
||||
@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();
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
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.
|
||||
@@ -22,6 +22,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -32,6 +33,9 @@ import org.springframework.context.annotation.Import;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(WireMockConfiguration.class)
|
||||
@PropertyMapping("wiremock.server")
|
||||
public @interface AutoConfigureWireMock {
|
||||
|
||||
int port() default 8080;
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
|
||||
import org.springframework.boot.context.embedded.Ssl;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
|
||||
@@ -125,11 +126,16 @@ class SpringBootHttpServer
|
||||
|
||||
@Override
|
||||
public int port() {
|
||||
return this.options.portNumber();
|
||||
if (options.httpsSettings().enabled()) {
|
||||
return options.portNumber();
|
||||
}
|
||||
EmbeddedWebApplicationContext embedded = (EmbeddedWebApplicationContext) context;
|
||||
return embedded.getEmbeddedServletContainer().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int httpsPort() {
|
||||
// TODO HTTPS on random port
|
||||
return this.options.httpsSettings().port();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class WireMockApplicationListener implements ApplicationListener<ApplicationPreparedEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationPreparedEvent event) {
|
||||
registerPort(event.getApplicationContext().getEnvironment());
|
||||
}
|
||||
|
||||
private void registerPort(ConfigurableEnvironment environment) {
|
||||
if (environment.getProperty("wiremock.server.port", Integer.class, 0) == 0) {
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
if (!propertySources.contains("wiremock")) {
|
||||
propertySources.addFirst(
|
||||
new MapPropertySource("wiremock", new HashMap<String, Object>()));
|
||||
}
|
||||
Map<String, Object> source = ((MapPropertySource) propertySources
|
||||
.get("wiremock")).getSource();
|
||||
source.put("wiremock.server.port", SocketUtils.findAvailableTcpPort(10000, 15000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -19,8 +19,12 @@ package org.springframework.cloud.contract.wiremock;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
@@ -31,7 +35,7 @@ import com.github.tomakehurst.wiremock.core.Options;
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
class WireMockConfiguration implements SmartLifecycle {
|
||||
public class WireMockConfiguration implements SmartLifecycle, ImportAware {
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
@@ -39,13 +43,31 @@ class WireMockConfiguration implements SmartLifecycle {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Options options;
|
||||
|
||||
@Value("${wiremock.server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata metadata) {
|
||||
int port = AnnotationAttributes
|
||||
.fromMap(metadata
|
||||
.getAnnotationAttributes(AutoConfigureWireMock.class.getName()))
|
||||
.getNumber("port").intValue();
|
||||
if (port>0) {
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (options == null) {
|
||||
this.options = com.github.tomakehurst.wiremock.core.WireMockConfiguration
|
||||
com.github.tomakehurst.wiremock.core.WireMockConfiguration factory = com.github.tomakehurst.wiremock.core.WireMockConfiguration
|
||||
.wireMockConfig()
|
||||
.httpServerFactory(new SpringBootHttpServerFactory());
|
||||
if (port != 8080) {
|
||||
factory.port(port);
|
||||
}
|
||||
this.options = factory;
|
||||
}
|
||||
server = new WireMockServer(options);
|
||||
}
|
||||
@@ -53,7 +75,7 @@ class WireMockConfiguration implements SmartLifecycle {
|
||||
@Override
|
||||
public void start() {
|
||||
server.start();
|
||||
WireMock.configureFor("localhost", options.portNumber());
|
||||
WireMock.configureFor("localhost", server.port());
|
||||
running = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Application Listeners
|
||||
org.springframework.context.ApplicationListener=\
|
||||
org.springframework.cloud.contract.wiremock.WireMockApplicationListener
|
||||
@@ -15,7 +15,7 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes=WiremockTestsApplication.class, properties="app.baseUrl=http://localhost:8080", webEnvironment=WebEnvironment.NONE)
|
||||
@SpringBootTest(classes=WiremockTestsApplication.class, properties="app.baseUrl=http://localhost:${wiremock.server.port}", webEnvironment=WebEnvironment.NONE)
|
||||
@DirtiesContext
|
||||
@AutoConfigureWireMock
|
||||
public class AutoConfigureWireMockApplicationTests {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
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 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;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = WiremockTestsApplication.class, properties = "app.baseUrl=http://localhost:${wiremock.server.port}", webEnvironment = WebEnvironment.NONE)
|
||||
@DirtiesContext
|
||||
@AutoConfigureWireMock(port = 9999)
|
||||
public class AutoConfigureWireMockPortApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
|
||||
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
||||
assertThat(this.service.go()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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 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;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes=WiremockTestsApplication.class, properties="app.baseUrl=http://localhost:${wiremock.server.port}", webEnvironment=WebEnvironment.NONE)
|
||||
@DirtiesContext
|
||||
@AutoConfigureWireMock(port=0)
|
||||
public class AutoConfigureWireMockRandomPortApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
||||
assertThat(this.service.go()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user