Add support for file root location in @AutoConfigureWireMock
This commit is contained in:
@@ -25,10 +25,12 @@ To start the stub server on a different port use `@AutoConfigureWireMock(port=99
|
||||
|
||||
=== Registering Stubs Automatically
|
||||
|
||||
If you add a `stubs` attribute to your `@AutoConfigureWireMock` then
|
||||
it will register WireMock JSON stubs from the file system or
|
||||
classpath. The stubs attribute can be a resource pattern (ant-style)
|
||||
or a directory, in which case `**/*.json` is appended. Example:
|
||||
If you use `@AutoConfigureWireMock` then it will register WireMock
|
||||
JSON stubs from the file system or classpath, by default from
|
||||
`file:src/test/resources/mappings`. You can customize the locations
|
||||
using the `stubs` attribute in the annotation, which can be a resource
|
||||
pattern (ant-style) or a directory, in which case `**/*.json` is
|
||||
appended. Example:
|
||||
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@@ -47,6 +49,30 @@ public class WiremockImportApplicationTests {
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Actually WireMock always loads mappings from
|
||||
`src/test/resources/mappings` *as well as* the custom locations in the
|
||||
stubs attribute. To change this behaviour you have to also specify a
|
||||
files root as described next.
|
||||
|
||||
=== Using Files to Specify the Stub Bodies
|
||||
|
||||
WireMock can read response bodies from files on the classpath or file
|
||||
system. In that case you will see in the JSON DSL that the response
|
||||
has a "bodyFileName" instead of a (literal) "body". The files are
|
||||
resolved relative to a root directory `src/test/resources/__files` by
|
||||
default. To customize this location you can set the `files` attribute
|
||||
in the `@AutoConfigureWireMock` annotation to the location of the
|
||||
parent directory (i.e. the place where `__files` is a
|
||||
subdirectory). You can use Spring resource notation to refer to
|
||||
`file:...` or `classpath:...` locations (but generic URLs are not
|
||||
supported). A list of values can be given and WireMock will resolve
|
||||
the first file that exists when it needs to find a response body.
|
||||
|
||||
NOTE: when you configure the `files` root, then it affects the
|
||||
automatic loading of stubs as well (they come from the root location
|
||||
in a subdirectory called "mappings"). The value of `files` has no
|
||||
effect on the stubs loaded explicitly from the `stubs` attribute.
|
||||
|
||||
=== Alternative: Using JUnit Rules
|
||||
|
||||
For a more conventional WireMock experience, using JUnit `@Rules` to
|
||||
|
||||
@@ -45,6 +45,8 @@ public @interface AutoConfigureWireMock {
|
||||
|
||||
int httpsPort() default -1;
|
||||
|
||||
String stubs() default "";
|
||||
String[] stubs() default {""};
|
||||
|
||||
String[] files() default {""};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@@ -25,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.contract.wiremock.file.ResourcesFileSource;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -72,14 +75,14 @@ public class WireMockConfiguration implements SmartLifecycle {
|
||||
@PostConstruct
|
||||
public void init() throws IOException {
|
||||
if (this.options == null) {
|
||||
com.github.tomakehurst.wiremock.core.WireMockConfiguration factory = WireMockSpring
|
||||
.options();
|
||||
com.github.tomakehurst.wiremock.core.WireMockConfiguration factory = WireMockSpring.options();
|
||||
if (this.wireMock.getPort() != 8080) {
|
||||
factory.port(this.wireMock.getPort());
|
||||
}
|
||||
if (this.wireMock.getHttpsPort() != -1) {
|
||||
factory.httpsPort(this.wireMock.getHttpsPort());
|
||||
}
|
||||
registerFiles(factory);
|
||||
this.options = factory;
|
||||
}
|
||||
this.server = new WireMockServer(this.options);
|
||||
@@ -90,19 +93,42 @@ public class WireMockConfiguration implements SmartLifecycle {
|
||||
}
|
||||
|
||||
private void registerStubs() throws IOException {
|
||||
if (StringUtils.hasText(this.wireMock.getStubs())) {
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
this.resourceLoader);
|
||||
String pattern = this.wireMock.getStubs();
|
||||
if (!pattern.contains("*")) {
|
||||
for (String stubs : this.wireMock.getStubs()) {
|
||||
if (StringUtils.hasText(stubs)) {
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
this.resourceLoader);
|
||||
String pattern = stubs;
|
||||
if (!pattern.contains("*")) {
|
||||
if (!pattern.endsWith("/")) {
|
||||
pattern = pattern + "/";
|
||||
}
|
||||
pattern = pattern + "**/*.json";
|
||||
}
|
||||
for (Resource resource : resolver.getResources(pattern)) {
|
||||
this.server.addStubMapping(StubMapping
|
||||
.buildFrom(StreamUtils.copyToString(resource.getInputStream(), Charset.forName("UTF-8"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerFiles(com.github.tomakehurst.wiremock.core.WireMockConfiguration factory) throws IOException {
|
||||
for (String files : this.wireMock.getFiles()) {
|
||||
if (StringUtils.hasText(files)) {
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
this.resourceLoader);
|
||||
String pattern = files;
|
||||
if (!pattern.endsWith("/")) {
|
||||
pattern = pattern + "/";
|
||||
}
|
||||
pattern = pattern + "**/*.json";
|
||||
}
|
||||
for (Resource resource : resolver.getResources(pattern)) {
|
||||
this.server.addStubMapping(StubMapping.buildFrom(StreamUtils.copyToString(
|
||||
resource.getInputStream(), Charset.forName("UTF-8"))));
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
for (Resource resource : resolver.getResources(pattern)) {
|
||||
if (resource.exists()) {
|
||||
resources.add(resource);
|
||||
}
|
||||
}
|
||||
ResourcesFileSource fileSource = new ResourcesFileSource(resources.toArray(new Resource[0]));
|
||||
factory.fileSource(fileSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +177,9 @@ class WireMockProperties {
|
||||
|
||||
private int httpsPort = -1;
|
||||
|
||||
private String stubs;
|
||||
private String[] stubs;
|
||||
|
||||
private String[] files;
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
@@ -169,12 +197,20 @@ class WireMockProperties {
|
||||
this.httpsPort = httpsPort;
|
||||
}
|
||||
|
||||
public String getStubs() {
|
||||
public String[] getStubs() {
|
||||
return this.stubs;
|
||||
}
|
||||
|
||||
public void setStubs(String stubs) {
|
||||
public void setStubs(String[] stubs) {
|
||||
this.stubs = stubs;
|
||||
}
|
||||
|
||||
public String[] getFiles() {
|
||||
return this.files;
|
||||
}
|
||||
|
||||
public void setFiles(String[] files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -232,7 +232,7 @@ public class WireMockRestServiceServer {
|
||||
if (!location.endsWith("/")) {
|
||||
location = location + "/";
|
||||
}
|
||||
for (Resource files : resolver.getResources(location)) {
|
||||
for (Resource files : this.resolver.getResources(location)) {
|
||||
if (files.exists()) {
|
||||
try {
|
||||
Resource resource = files.createRelative(file);
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.file;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
|
||||
import com.github.tomakehurst.wiremock.common.BinaryFile;
|
||||
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
|
||||
import com.github.tomakehurst.wiremock.common.FileSource;
|
||||
import com.github.tomakehurst.wiremock.common.SingleRootFileSource;
|
||||
import com.github.tomakehurst.wiremock.common.TextFile;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ResourcesFileSource implements FileSource {
|
||||
|
||||
private final FileSource[] sources;
|
||||
|
||||
public ResourcesFileSource(Resource[] resources) {
|
||||
this.sources = new FileSource[resources.length];
|
||||
for (int i = 0; i < resources.length; i++) {
|
||||
Resource resource = resources[i];
|
||||
if (resource instanceof ClassPathResource) {
|
||||
ClassPathResource classes = (ClassPathResource) resource;
|
||||
this.sources[i] = new ClasspathFileSource(classes.getPath());
|
||||
}
|
||||
else if (resource instanceof FileSystemResource) {
|
||||
FileSystemResource files = (FileSystemResource) resource;
|
||||
this.sources[i] = new SingleRootFileSource(files.getFile());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported resource type for file source: " + resource.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryFile getBinaryFileNamed(String name) {
|
||||
for (FileSource resource : this.sources) {
|
||||
try {
|
||||
UrlResource uri = new UrlResource(resource.getBinaryFileNamed(name).getUri());
|
||||
if (uri.exists()) {
|
||||
return resource.getBinaryFileNamed(name);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Cannot create file for " + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createIfNecessary() {
|
||||
throw new UnsupportedOperationException("Resource file sources are read-only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSource child(String subDirectoryName) {
|
||||
for (FileSource resource : this.sources) {
|
||||
try {
|
||||
UrlResource uri = new UrlResource(resource.child(subDirectoryName).getUri());
|
||||
if (uri.createRelative(subDirectoryName).exists()) {
|
||||
return resource.child(subDirectoryName);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return this.sources[0].child(subDirectoryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
for (FileSource resource : this.sources) {
|
||||
try {
|
||||
UrlResource uri = new UrlResource(resource.getUri());
|
||||
if (uri.exists()) {
|
||||
return resource.getPath();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return this.sources[0].getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
for (FileSource resource : this.sources) {
|
||||
try {
|
||||
UrlResource uri = new UrlResource(resource.getUri());
|
||||
if (uri.exists()) {
|
||||
return resource.getUri();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return this.sources[0].getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextFile> listFilesRecursively() {
|
||||
List<TextFile> files = new ArrayList<>();
|
||||
for (FileSource resource : this.sources) {
|
||||
files.addAll(resource.listFilesRecursively());
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTextFile(String name, String contents) {
|
||||
throw new UnsupportedOperationException("Resource file sources are read-only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBinaryFile(String name, byte[] contents) {
|
||||
throw new UnsupportedOperationException("Resource file sources are read-only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
for (FileSource resource : this.sources) {
|
||||
if (resource.exists()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
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, files="classpath:/root/")
|
||||
public class AutoConfigureWireMockFilesApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
assertThat(this.service.go()).isEqualTo("{\"message\":\"Hello Root\"}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
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, files="classpath:/root/", stubs="file:src/test/resources/io.stubs/mappings")
|
||||
public class AutoConfigureWireMockStubsAndFilesApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
assertThat(this.service.go()).isEqualTo("{\"message\":\"Hello Root\"}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
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, files={"classpath:/nonexistent/", "classpath:/root/"}, stubs="file:src/test/resources/io.stubs/mappings")
|
||||
public class AutoConfigureWireMockStubsAndMultipleFilesApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
assertThat(this.service.go()).isEqualTo("{\"message\":\"Hello Root\"}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"message":"Hello Root"}
|
||||
@@ -0,0 +1 @@
|
||||
{"message":"Hello Root"}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/test",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"bodyFileName" : "hello.json"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user