概要
この記事は、ユニットテスト環境にJUnit5とMockサーバにWireMockを使ったプロジェクトのひな型を作った時のメモです。
環境
- Windows 10 Professional
- OpenJDK 11
- JUnit5 5.3.2
- WireMock 2.20.0
- Maven 3.6.0
- Eclipse 2018-09
参考
- JUnit5
- [JUnit 5 User Guide] (https://junit.org/junit5/docs/current/user-guide/)
- WireMock
- [WireMock] (http://wiremock.org/)
- [WireMock JUnit 5 and Rest-Assured Example] (https://www.swtestacademy.com/wiremock-junit-5-rest-assured/)
- REST Assured
- [Usage rest-assured/rest-assured] (https://github.com/rest-assured/rest-assured/wiki/Usage)
JDK 11
> java -version
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
Maven
[Release Notes – Maven – Version 3.6.0] (https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12316922&version=12338966)
D:\dev\apache-maven-3.6.0> bin\mvn.cmd -v
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T03:41:47+09:00)
Maven home: D:\dev\apache-maven-3.6.0\bin\..
Java version: 11.0.1, vendor: Oracle Corporation, runtime: D:\openjdk\openjdk-11.0.1_windows-x64
Default locale: ja_JP, platform encoding: MS932
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
pom.xml
最終的なpom.xmlの内容を掲載します。
<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>
<groupId>com.example</groupId>
<artifactId>wiremock-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>wiremock-demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<!-- compiler -->
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
<maven.compiler.showWarnings>true</maven.compiler.showWarnings>
<maven.compiler.verbose>true</maven.compiler.verbose>
<!-- JUnit5 -->
<junit.jupiter.version>5.3.2</junit.jupiter.version>
<junit.platform.version>1.3.2</junit.platform.version>
<!-- WireMock -->
<wiremock.version>2.20.0</wiremock.version>
<!-- REST Assured -->
<rest-assured.version>3.2.0</rest-assured.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit.platform.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.tomakehurst/wiremock -->
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.20.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>demo</finalName>
<plugins>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-clean-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<compilerArgs>
<arg>-Xlint:all</arg>
</compilerArgs>
<release>${java.version}</release>
</configuration>
</plugin>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-resources-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-jar-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-surefire-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<!-- <configuration> <forkCount>0</forkCount> </configuration> -->
</plugin>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-failsafe-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
</plugins>
</build>
</project>
JUnit5
[junit-jupiter-engine] (https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine)
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
[junit-platform-launcher] (https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher)
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.3.2</version>
<scope>test</scope>
</dependency>
[WireMock] (http://wiremock.org/)
[wiremock] (https://mvnrepository.com/artifact/com.github.tomakehurst/wiremock)
<!-- https://mvnrepository.com/artifact/com.github.tomakehurst/wiremock -->
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.20.0</version>
<type>pom</type>
<scope>test</scope>
</dependency>
[wiremock-junit5] (https://mvnrepository.com/artifact/ru.lanwen.wiremock/wiremock-junit5)
今回は使いませんでしたが、Third partyのJUnit5拡張機能もあります。
<!-- https://mvnrepository.com/artifact/ru.lanwen.wiremock/wiremock-junit5 -->
<dependency>
<groupId>ru.lanwen.wiremock</groupId>
<artifactId>wiremock-junit5</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
[REST Assured] (http://rest-assured.io/)
WireMockのスタブの動作確認用にREST Assuredを使用しました。
[rest-assured] (https://mvnrepository.com/artifact/io.rest-assured/rest-assured)
<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.2.0</version>
<scope>test</scope>
</dependency>
WireMockの基本的な使い方
WireMockServer
現時点(2019/01)でJUnit5の拡張機能は用意されていないので、[Java (Non-JUnit) Usage] (http://wiremock.org/docs/java-usage/)を参考に、WireMockサーバをプログラム的(programmatically)に起動、停止させます。
@DisplayName("WireMock Test Demo")
public class WireMockDemoTests {
private WireMockServer server;
private RestAssuredConfig config;
@BeforeEach
void setup() {
// REST Assured configuration
config = config().encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false))
.logConfig(logConfig().enablePrettyPrinting(true));
server = new WireMockServer();
server.start();
}
@AfterEach
void tearDown() {
server.stop();
}
}
[Configuration] (http://wiremock.org/docs/configuration/)
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
// ConsoleNotifier
Notifier notifier = new ConsoleNotifier(true);
// Slf4jNotifier
// Notifier notifier = new Slf4jNotifier(true);
Options options = options().port(8080).notifier(notifier);
server = new WireMockServer(options);
[Stubbing] (http://wiremock.org/docs/stubbing/)
スタブの登録方法
基本的なスタブの登録
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
@Test
@DisplayName("wiremock demo #1")
void mockDemo1() {
// stub (WireMock)
server.stubFor(get(urlEqualTo("/mock/demo/1")).withName("mock_demo_1")
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!"))
);
// exercise and assert (REST Assured)
given()
.config(config)
.header("Content-Type", "text/plain")
.when()
.get("http://localhost:8080/mock/demo/1")
.then()
.assertThat()
.contentType(ContentType.TEXT)
.statusCode(200)
.header("Matched-Stub-Name", "mock_demo_1")
.body(Matchers.containsString("Hello world!"));
// verify (WireMock)
server.verify(getRequestedFor(urlEqualTo("/mock/demo/1"))
.withHeader("Content-Type", equalTo("text/plain")));
}
マッピングファイルを使う
デフォルトではsrc/test/resources/mappings
ディレクトリ下に配置するマッピングファイル(拡張子がjson
)からスタブのマッピングが登録されます。
マッピングファイルの例
requestとresponseにスタブの振る舞いを記述します。
nameはオプションですが明記しておくとMatched-Stub-Name
というレスポンスヘッダーにnameがセットされるようになります。
{
"name": "mock_demo_2",
"request": {
"method": "GET",
"url": "/mock/demo/2",
"headers": {
"Content-Type": {
"matches": "application/json"
}
}
},
"response": {
"status": 200,
"body": "{\"name\": \"John\"}",
"headers": {
"Content-Type": "application/json"
}
}
}
マッピングファイルを利用することで、テストにスタブの設定コードを記述しなくても済むようになります。
@Test
@DisplayName("wiremock demo #2")
void mockDemo2() {
// exercise and assert (REST Assured)
given()
.config(config)
.header("Content-Type", "application/json")
.when()
.get("http://localhost:8080/mock/demo/2")
.then()
.assertThat()
.contentType(ContentType.JSON)
.statusCode(200)
.header("Matched-Stub-Name", "mock_demo_2")
.body("name", Matchers.equalTo("John"));
// verify (WireMock)
server.verify(getRequestedFor(urlEqualTo("/mock/demo/2"))
.withHeader("Content-Type", equalTo("application/json")));
}
Response Bodyに外部ファイルを利用する
Response Bodyの内容に外部ファイルを利用したい場合、bodyの代わりにbodyFileNameにファイル名を指定します。
{
"name": "mock_demo_3",
"request": {
"method": "GET",
"url": "/mock/demo/3",
"headers": {
"Content-Type": {
"matches": "application/json"
}
}
},
"response": {
"status": 200,
"bodyFileName": "loto.json",
"headers": {
"Content-Type": "application/json"
}
}
}
デフォルトのファイルの設置場所はsrc/test/resources/__files
になります。
{
"lotto": {
"lottoId": 5,
"winning-numbers": [
2,
45,
34,
23,
7,
5,
3
],
"winners": [
{
"winnerId": 23,
"numbers": [
2,
45,
34,
23,
3,
5
]
},
{
"winnerId": 54,
"numbers": [
52,
3,
12,
11,
18,
22
]
}
]
}
}