Skip to content

Commit 983ede3

Browse files
Merge pull request #1 from oleg-nenashev/wiremock-extensions
Introduce support for WireMock extensions
2 parents dfa9d6e + 7031d5c commit 983ede3

File tree

7 files changed

+322
-23
lines changed

7 files changed

+322
-23
lines changed

.github/workflows/maven.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ on:
1616

1717
jobs:
1818
build:
19-
2019
runs-on: ubuntu-latest
21-
2220
steps:
2321
- uses: actions/checkout@v3
2422
- name: Set up JDK 11
@@ -29,7 +27,3 @@ jobs:
2927
cache: maven
3028
- name: Build with Maven
3129
run: mvn -B package --file pom.xml
32-
33-
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
34-
- name: Update dependency graph
35-
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6

README.md

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ A common example is using Wiremock 3.x with Java 1.8.
1717

1818
## Usage
1919

20-
Import the dependency:
20+
### Importing the dependency
2121

2222
```xml
2323
<dependency>
@@ -28,8 +28,9 @@ Import the dependency:
2828
</dependency>
2929
```
3030

31-
Use it in your Unit tests.
32-
Javadoc is coming soon!
31+
### Using the test container in JUnit 4/5
32+
33+
P.S: Javadoc is coming soon!
3334

3435
```java
3536
import org.wiremock.integrations.testcontainers.WireMockContainer;
@@ -49,10 +50,8 @@ public class WireMockContainerTest {
4950

5051
@Test
5152
public void helloWorld() throws Exception {
52-
final HttpClient client = HttpClient.newBuilder()
53-
.version(HttpClient.Version.HTTP_1_1).build();
54-
55-
HttpRequest request = HttpRequest.newBuilder()
53+
final HttpClient client = HttpClient.newBuilder().build();
54+
final HttpRequest request = HttpRequest.newBuilder()
5655
.uri(wiremockServer.getRequestURI("hello"))
5756
.timeout(Duration.ofSeconds(10))
5857
.header("Content-Type", "application/json")
@@ -68,6 +67,102 @@ public class WireMockContainerTest {
6867
}
6968
```
7069

70+
### Using WireMock extensions
71+
72+
The API supports adding [WireMock extensions](https://wiremock.org/docs/extending-wiremock/)
73+
to the test container.
74+
The extension can be sourced from the classpath for bundled extensions,
75+
or added from the JAR file in the initializer.
76+
77+
#### Using external extensions
78+
79+
For the external extensions,
80+
an extension Jar should be pulled to the test directory before running the test.
81+
[Apache Maven Dependency Plugin](https://maven.apache.org/plugins/maven-dependency-plugin/) can be used for this purpose.
82+
Make sure that all dependencies of the extension JAR, if any,
83+
are also included.
84+
85+
Below you can see an examples of using the _JSON Body Transformer_ extension
86+
from the [9cookies/wiremock-extensions](https://github.com/9cookies/wiremock-extensions).
87+
88+
Copying the dependency:
89+
90+
```xml
91+
<plugin>
92+
<groupId>org.apache.maven.plugins</groupId>
93+
<artifactId>maven-dependency-plugin</artifactId>
94+
<version>3.5.0</version>
95+
<executions>
96+
<execution>
97+
<id>copy</id>
98+
<phase>package</phase>
99+
<goals>
100+
<goal>copy</goal>
101+
</goals>
102+
<configuration>
103+
<artifactItems>
104+
<artifactItem>
105+
<groupId>com.ninecookies.wiremock.extensions</groupId>
106+
<artifactId>wiremock-extensions</artifactId>
107+
<version>0.4.1</version>
108+
<classifier>jar-with-dependencies</classifier>
109+
</artifactItem>
110+
</artifactItems>
111+
<outputDirectory>${project.build.directory}/test-wiremock-extension</outputDirectory>
112+
</configuration>
113+
</execution>
114+
</executions>
115+
</plugin>
116+
```
117+
118+
Mapping definition:
119+
120+
```json
121+
{
122+
"request": {
123+
"method": "POST",
124+
"url": "/json-body-transformer"
125+
},
126+
"response": {
127+
"status": 201,
128+
"headers": {
129+
"content-type": "application/json"
130+
},
131+
"jsonBody": {
132+
"message": "Hello, $(name)!"
133+
},
134+
"transformers" : ["json-body-transformer"]
135+
}
136+
}
137+
```
138+
139+
Test sample:
140+
141+
```java
142+
public class WireMockContainerExtensionTest {
143+
@Rule
144+
public WireMockContainer wiremockServer = new WireMockContainer("2.35.0")
145+
.withMapping("json-body-transformer", WireMockContainerExtensionTest.class, "json-body-transformer.json")
146+
.withExtension("JSON Body Transformer", Collections.singleton("com.ninecookies.wiremock.extensions.JsonBodyTransformer"),
147+
Collections.singleton(Paths.get("target", "test-wiremock-extension", "9cookies-wiremock-extensions.jar").toFile()));
148+
149+
@Test
150+
public void testJSONBodyTransformer() throws Exception {
151+
final HttpClient client = HttpClient.newBuilder().build();
152+
final HttpRequest request = HttpRequest.newBuilder()
153+
.uri(wiremockServer.getRequestURI("json-body-transformer"))
154+
.timeout(Duration.ofSeconds(10))
155+
.header("Content-Type", "application/json")
156+
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"John Doe\"}")).build();
157+
158+
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
159+
160+
assertThat(response.body()).as("Wrong response body")
161+
.contains("Hello, John Doe!");
162+
}
163+
}
164+
```
165+
71166
## Contributing
72167

73168
All contributions are welcome!

pom.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,45 @@
103103
</execution>
104104
</executions>
105105
</plugin>
106+
107+
<!-- For testing WireMock extensions -->
108+
<plugin>
109+
<groupId>org.apache.maven.plugins</groupId>
110+
<artifactId>maven-dependency-plugin</artifactId>
111+
<version>3.5.0</version>
112+
<executions>
113+
<execution>
114+
<id>copy</id>
115+
<phase>package</phase>
116+
<goals>
117+
<goal>copy</goal>
118+
</goals>
119+
<configuration>
120+
<artifactItems>
121+
<artifactItem>
122+
<groupId>com.ninecookies.wiremock.extensions</groupId>
123+
<artifactId>wiremock-extensions</artifactId>
124+
<version>0.4.1</version>
125+
<classifier>jar-with-dependencies</classifier>
126+
</artifactItem>
127+
</artifactItems>
128+
<outputDirectory>${project.build.directory}/test-wiremock-extension</outputDirectory>
129+
</configuration>
130+
</execution>
131+
</executions>
132+
</plugin>
106133
</plugins>
107134
</build>
108135

136+
<!-- For testing WireMock extensions -->
137+
<repositories>
138+
<repository>
139+
<id>9c-releases</id>
140+
<url>https://raw.github.com/9cookies/mvn-repo/master/releases/</url>
141+
<releases>
142+
<enabled>true</enabled>
143+
<updatePolicy>always</updatePolicy>
144+
</releases>
145+
</repository>
146+
</repositories>
109147
</project>

src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@
2121
import java.net.URISyntaxException;
2222
import java.net.URL;
2323
import java.nio.charset.StandardCharsets;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.util.ArrayList;
27+
import java.util.Collection;
28+
import java.util.Collections;
2429
import java.util.HashMap;
30+
import java.util.List;
2531
import java.util.Map;
32+
import java.util.stream.Collectors;
33+
import java.util.stream.Stream;
2634

2735
import org.testcontainers.containers.GenericContainer;
2836
import org.testcontainers.images.builder.Transferable;
@@ -31,6 +39,8 @@
3139

3240
/**
3341
* Provisions WireMock standalone server as a container.
42+
* Designed to follow the WireMock Docker image ({@code wiremock/wiremock}) structure and configuration,
43+
* but other images can be included too at your own risk.
3444
*/
3545
public class WireMockContainer extends GenericContainer<WireMockContainer> {
3646
private static final String DEFAULT_IMAGE_NAME = "wiremock/wiremock";
@@ -39,12 +49,15 @@ public class WireMockContainer extends GenericContainer<WireMockContainer> {
3949
private static final String MAPPINGS_DIR = "/home/wiremock/mappings/";
4050
private static final String FILES_DIR = "/home/wiremock/__files/";
4151

52+
private static final String EXTENSIONS_DIR = "/var/wiremock/extensions/";
53+
4254
private static final int PORT = 8080;
4355

4456
private final StringBuilder wireMockArgs;
4557

4658
private final Map<String, Stub> mappingStubs = new HashMap<>();
4759
private final Map<String, MountableFile> mappingFiles = new HashMap<>();
60+
private final Map<String, Extension> extensions = new HashMap<>();
4861

4962
public WireMockContainer() {
5063
this(DEFAULT_TAG);
@@ -115,6 +128,55 @@ public WireMockContainer withFileFromResource(String name, Class<?> resource, St
115128
return withFileFromResource(name, resource.getName().replace('.', '/') + "/" + filename);
116129
}
117130

131+
/**
132+
* Add extension that will be loaded from the specified JAR file.
133+
* @param id Unique ID of the extension, for logging purposes
134+
* @param classNames Class names of the extension to be included
135+
* @param jars JARs to be included into the container
136+
* @return this instance
137+
*/
138+
public WireMockContainer withExtension(String id, Collection<String> classNames, Collection<File> jars) {
139+
final Extension extension = new Extension(id);
140+
extension.extensionClassNames.addAll(classNames);
141+
extension.jars.addAll(jars);
142+
extensions.put(id, extension);
143+
return this;
144+
}
145+
146+
/**
147+
* Add extension that will be loaded from the specified directory with JAR files.
148+
* @param id Unique ID of the extension, for logging purposes
149+
* @param classNames Class names of the extension to be included
150+
* @param jarDirectory Directory that stores all JARs
151+
* @return this instance
152+
*/
153+
public WireMockContainer withExtension(String id, Collection<String> classNames, File jarDirectory) {
154+
final List<File> jarsInTheDirectory;
155+
try (Stream<Path> walk = Files.walk(jarDirectory.toPath())) {
156+
jarsInTheDirectory = walk
157+
.filter(p -> !Files.isDirectory(p))
158+
.map(Path::toFile)
159+
.filter(f -> f.toString().endsWith(".jar"))
160+
.collect(Collectors.toList());
161+
} catch (IOException e) {
162+
throw new IllegalArgumentException("Cannot list JARs in the directory " + jarDirectory, e);
163+
}
164+
165+
return withExtension(id, classNames, jarsInTheDirectory);
166+
}
167+
168+
/**
169+
* Add extension that will be loaded from the classpath.
170+
* This method can be used if the extension is a part of the WireMock bundle,
171+
* or a Jar is already added via {@link #withExtension(String, Collection, Collection)}}
172+
* @param id Unique ID of the extension, for logging purposes
173+
* @param className Class name of the extension
174+
* @return this instance
175+
*/
176+
public WireMockContainer withExtension(String id, String className) {
177+
return withExtension(id, Collections.singleton(className), Collections.emptyList());
178+
}
179+
118180
public String getEndpoint() {
119181
return String.format("http://%s:%d", getHost(), getMappedPort(PORT));
120182
}
@@ -131,14 +193,33 @@ public Integer getServerPort() {
131193
protected void configure() {
132194
super.configure();
133195
withExposedPorts(PORT);
134-
withCommand(wireMockArgs.toString());
135196
for (Stub stub : mappingStubs.values()) {
136197
withCopyToContainer(Transferable.of(stub.json), MAPPINGS_DIR + stub.name + ".json");
137198
}
138199

139200
for (Map.Entry<String, MountableFile> mount : mappingFiles.entrySet()) {
140201
withCopyToContainer(mount.getValue(), FILES_DIR + mount.getKey());
141202
}
203+
204+
final ArrayList<String> extensionClassNames = new ArrayList<>();
205+
for (Map.Entry<String, Extension> entry : extensions.entrySet()) {
206+
final Extension ext = entry.getValue();
207+
extensionClassNames.addAll(ext.extensionClassNames);
208+
for (File jar : ext.jars) {
209+
withCopyToContainer(MountableFile.forHostPath(jar.toPath()), EXTENSIONS_DIR + jar.getName());
210+
}
211+
}
212+
if (!extensionClassNames.isEmpty()) {
213+
wireMockArgs.append(" --extensions ");
214+
int counter = 0;
215+
for (String className : extensionClassNames) {
216+
wireMockArgs.append(className);
217+
wireMockArgs.append( (++counter != extensionClassNames.size()) ? ',' : ' ');
218+
}
219+
}
220+
221+
// Add CLI arguments
222+
withCommand(wireMockArgs.toString());
142223
}
143224

144225
private static final class Stub {
@@ -151,4 +232,14 @@ public Stub (String name, String json) {
151232
}
152233
}
153234

235+
private static final class Extension {
236+
final String id;
237+
final List<File> jars = new ArrayList<>();
238+
final List<String> extensionClassNames = new ArrayList<>();
239+
240+
public Extension(String id) {
241+
this.id = id;
242+
}
243+
}
244+
154245
}

0 commit comments

Comments
 (0)