今回は「Spring Framework 5.0 主な変更点」シリーズの第5回で、Test関連の主な変更点(新機能や改善点など)を紹介していきたいと思います。
シリーズ
- 第1回: Spring Framework 5.0 主な変更点の概要
- 第2回: Spring Framework 5.0 コア機能の主な変更点
- 第3回: Spring Framework 5.0 DIコンテナ関連の主な変更点
- 第4回: Spring Framework 5.0 WebMVC関連の主な変更点
- 第5回: Spring Framework 5.0 Test関連の主な変更点
- 第6回: Spring Framework 5.0 WebFlux(Reactive Web Framework) (予定)
- 第7回: Spring Framework 5.0 Kotlinサポート (予定)
動作検証バージョン
- Spring Framework 5.0.0.RC1
- Junit Jupiter 5.0.0.M4
- Junit Platform 1.0.0.M4
- Spring Boot 2.0.0.M1
- JDK 1.8.0_121
- Mac
Test関連の変更点
Spring Framework 5.0では、テスト機能に対して以下のような変更が行われています。
項番 | 変更内容 |
---|---|
1 | Spring TestContext Framework(TCF)をJUnit 5 Jupiter上で利用できるようになります。 [詳細へ] Note: SpringExtension クラス、@SpringJUnitConfig アノテーション、@SpringJUnitWebConfig アノテーション、@EnabledIf アノテーション、@DisabledIf アノテーションが追加されます。 |
2 | Spring TestContext Frameworkを使用したテストを平行実行できるようになります。 [詳細へ] |
3 |
TestExecutionListener インタフェースに、テストの実行直前と実行直後に呼び出されるコールバックメソッド(beforeTestExecution とafterTestExecution )が追加されます。 [詳細へ] |
4 |
MockHttpServletRequest にリクエストBODYにアクセスするためのメソッド(getContentAsByteArray とgetContentAsString )が追加されます。 [詳細へ] |
5 |
MockMvc 使用時のテスト結果をコンソールまたはログに出力する際(MockMvcResultHandlers のprint またはlog メソッドを使用する際)に、リクエストBODYも出力されるようになります。 [詳細へ] |
6 |
MockMvc 使用時の「リダイレクト先URL」と「フォワード先URL」を検証する際に、期待値にURIテンプレートを指定できるようになります。 [詳細へ] |
7 | XMLの検証をサポートするライブラリであるXMLUnitのサポートバージョンが2.3になります。 [詳細へ] |
JUnit 5がサポートされる
[SPR-13575] : Spring Framework 5.0におけるテスト関連の変更点の目玉は、なんといってもJUnit 5 Jupiter上でSpring TestContext Framework(TCF)が利用できるように対応されることでしょう。具体的には・・・
クラス(アノテーション) | 説明 |
---|---|
SpringExtension |
Junit 5が提供している「拡張ポイント(Extension )」を利用して、Junit 5上でSpring TestContext Frameworkを使えるようにしているクラスです。 JUnit 4上では SpringRunner 、SpringClassRule やSpringMethodRule が同じ役割を担っています。 |
@SpringJUnitConfig |
Junit 5上でSpring TestContext Frameworkを利用することを示すための合成アノテーション(@ExtendWith(SpringExtension.class) + @ContextConfiguration )です。 |
@SpringJUnitWebConfig |
Junit 5上でWEB環境向けのSpring TestContext Frameworkを利用することを示すための合成アノテーション(@ExtendWith(SpringExtension.class) + @ContextConfiguration + @WebAppConfiguration )です。 |
@EnabledIf |
指定した条件を充す(SpELで指定したExpressionの結果がtrue になる)場合にテストを実行することを示すアノテーションです。 |
@DisabledIf |
指定した条件を充す(SpELで指定したExpressionの結果がtrue になる)場合にテストをスキップすることを示すアノテーションです。 |
が追加されます。
なお、TCF自体の機能はJUnit 4上でもJUnit 5上でも同じなので、TCFの機能に対する説明は本エントリーでは割愛します。あくまで、TCFを使えるようにするための方法などが変わるだけです。
Junit 5でテストを動かしてみよう
本エントリーではJunit 5の説明は基本的には行いませんが、Junit 5上でテストするための環境は必要です。ということで・・・とりあえずJunit 5を使うMavenプロジェクトを作成して簡単なテストを実行してみます。
まず、任意のディレクトリ(例: /usr/local/apps/spring5-test-demo
)を作成し、そのディレクトリ内のディレクトリ構成を以下のようにします。
$ mkdir -p /usr/local/apps/spring5-test-demo
$ cd /usr/local/apps/spring5-test-demo
...
$ tree
.
└── src
├── main
│ ├── java
│ └── resources
└── test
├── java
└── resources
つぎに、pom.xmlを作成します。
<?xml version="1.0" encoding="UTF-8"?>
<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.spring5testdemo</groupId>
<artifactId>spring5-test-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit-jupiter.version>5.0.0-M4</junit-jupiter.version>
<junit-platform.version>1.0.0-M4</junit-platform.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version> <!-- 執筆時点の最新は2.20でしたが、テスト失敗時にOutOfMemoryErrorがでるためバージョンを下げています・・・ -->
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit-platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
Note:
maven-surefire-plugin 2.20 + JUnit 5でOutOfMemoryError
がでる件は、次のマイルストーンリリース(5.0.0.M5)で解消される予定です。
最後にテストクラスを作成します。
package com.example.spring5testdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SimpleJunit5Test {
@Test
void simpleTest() {
Assertions.assertEquals("test", "test");
}
}
このプロジェクトをJunit 5をサポートしているIDEにインポートし、IDEの機能を使ってJunit 5上でテストを実行することもできますが、ここではMavenプラグイン(maven-surefire-plugin)を使用してテストを実行してみます。
$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring5-test-demo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring5-test-demo ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring5-test-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ spring5-test-demo ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /usr/local/apps/spring5-test-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ spring5-test-demo ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /usr/local/apps/spring5-test-demo/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.19.1:test (default-test) @ spring5-test-demo ---
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.spring5testdemo.SimpleJunit5Test
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.035 sec - in com.example.spring5testdemo.SimpleJunit5Test
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.532 s
[INFO] Finished at: 2017-05-20T21:49:02+09:00
[INFO] Final Memory: 17M/300M
[INFO] ------------------------------------------------------------------------
ためしにテストが失敗するようにして再実行してみましょう。
@Test
void simpleTest() {
Assertions.assertEquals("test", "fail"); // 必ずエラーになる
}
$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring5-test-demo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring5-test-demo ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring5-test-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ spring5-test-demo ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /usr/local/apps/spring5-test-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ spring5-test-demo ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /usr/local/apps/spring5-test-demo/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.19.1:test (default-test) @ spring5-test-demo ---
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.spring5testdemo.SimpleJunit5Test
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.043 sec <<< FAILURE! - in com.example.spring5testdemo.SimpleJunit5Test
simpleTest() Time elapsed: 0.02 sec <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <test> but was: <fail>
at com.example.spring5testdemo.SimpleJunit5Test.simpleTest(SimpleJunit5Test.java:10)
Results :
Failed tests:
SimpleJunit5Test.simpleTest:10 expected: <test> but was: <fail>
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.510 s
[INFO] Finished at: 2017-05-20T21:50:21+09:00
[INFO] Final Memory: 17M/257M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test (default-test) on project spring5-test-demo: There are test failures.
[ERROR]
[ERROR] Please refer to /usr/local/apps/spring5-test-demo/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
SpringExtension
を試す
JUnit 5上でSpring TestContext Frameworkを利用する場合は、SpringExtension
の指定が必要になりますが、まずSpring Testを使えるようにするために、pom.xmlを以下のように修正する必要があります。
<?xml version="1.0" encoding="UTF-8"?>
<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.spring5testdemo</groupId>
<artifactId>spring5-test-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit-jupiter.version>5.0.0-M4</junit-jupiter.version>
<junit-platform.version>1.0.0-M4</junit-platform.version>
</properties>
<!-- ★★★ Spring Frameworkのbom(bill of materials)を追加する(bom側でバージョン番号を管理) -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.0.0.RC1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- ★★★ spring-context + spring-testを追加する -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ★★★ ログ出力用のライブラリ(logback)を追加する -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit-platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<!-- ★★★ Springのスナップショットとマイルストーンバージョンが格納されているMavenリポジトリをそれぞれ追加する -->
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
</project>
Note:
ログ出力ライブラリを追加しないとmaven-surefire-pluginを使ってテストを実行する際にエラーになってしまいます。なぜエラーになるのか?なぜライブラリ追加するとエラーが出ないのかわからいので・・・とりあえずSpringのJIRA(SPR-15572)をあげておきました。ちなみにIntelliJの機能でテストを実行する際はエラーになりませんでした。(Eclipse/STSでは試してません)
ライブラリの追加が終わったら、実際にテストクラスを作成してみましょう。
package com.example.spring5testdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class) // ★★★ SpringExtensionを指定
class SpringTcfOnJunit5Test {
@Autowired private ApplicationContext applicationContext;
@Test
void defaultContextTest() {
Assertions.assertEquals(1, applicationContext.getBeansOfType(MessageSource.class).size());
}
// 何かしらBean定義ファイル(XML or JavaConfig)が必要なので、ここではstaticインナークラスとしてJavaConfigクラスを作っておく
// staticインナークラスとしてJavaConfigクラスを作っておくとTCFが自動検出してくれる
@Configuration
static class LocalTestContext {}
}
テストを実行すると・・・以下のようなログが出力され、Spring TCFがApplicationContext
(DIコンテナ)を生成していることがわかります。
$ mvn test
...
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.spring5testdemo.SimpleJunit5Test
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.045 sec - in com.example.spring5testdemo.SimpleJunit5Test
Running com.example.spring5testdemo.SpringTcfOnJunit5Test
00:29:19.216 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
00:29:19.230 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
00:29:19.241 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.example.spring5testdemo.SpringTcfOnJunit5Test] from class [org.springframework.test.context.support.DefaultTestContextBootstrapper]
00:29:19.254 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.example.spring5testdemo.SpringTcfOnJunit5Test], using DelegatingSmartContextLoader
00:29:19.258 [main] DEBUG org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - Delegating to GenericXmlContextLoader to process context configuration [ContextConfigurationAttributes@2f0a87b3 declaringClass = 'com.example.spring5testdemo.SpringTcfOnJunit5Test', classes = '{}', locations = '{}', inheritLocations = false, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
00:29:19.261 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.example.spring5testdemo.SpringTcfOnJunit5Test]: class path resource [com/example/spring5testdemo/SpringTcfOnJunit5Test-context.xml] does not exist
00:29:19.261 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.example.spring5testdemo.SpringTcfOnJunit5Test]: no resource found for suffixes {-context.xml}.
00:29:19.262 [main] DEBUG org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - Delegating to AnnotationConfigContextLoader to process context configuration [ContextConfigurationAttributes@2f0a87b3 declaringClass = 'com.example.spring5testdemo.SpringTcfOnJunit5Test', classes = '{}', locations = '{}', inheritLocations = false, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
00:29:19.265 [main] INFO org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - AnnotationConfigContextLoader detected default configuration classes for context configuration [ContextConfigurationAttributes@2f0a87b3 declaringClass = 'com.example.spring5testdemo.SpringTcfOnJunit5Test', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', locations = '{}', inheritLocations = false, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
00:29:19.276 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.example.spring5testdemo.SpringTcfOnJunit5Test]
00:29:19.278 [main] DEBUG org.springframework.test.context.support.DefaultTestContextBootstrapper - @TestExecutionListeners is not present for class [com.example.spring5testdemo.SpringTcfOnJunit5Test]: using defaults.
00:29:19.279 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
00:29:19.286 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
00:29:19.287 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
00:29:19.287 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
00:29:19.288 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@d6da883, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@45afc369, org.springframework.test.context.support.DirtiesContextTestExecutionListener@799d4f69]
00:29:19.289 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@69a10787 testClass = SpringTcfOnJunit5Test, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].
00:29:19.290 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@69a10787 testClass = SpringTcfOnJunit5Test, testInstance = com.example.spring5testdemo.SpringTcfOnJunit5Test@11c20519, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map[[empty]]]].
00:29:19.290 [main] DEBUG org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - Delegating to AnnotationConfigContextLoader to load context from [MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]].
00:29:19.291 [main] DEBUG org.springframework.test.context.support.AbstractGenericContextLoader - Loading ApplicationContext for merged context configuration [[MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
00:29:19.331 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
00:29:19.332 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
00:29:19.332 [main] DEBUG org.springframework.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
00:29:19.332 [main] DEBUG org.springframework.test.context.support.AnnotationConfigContextLoader - Registering annotated classes: {class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}
00:29:19.362 [main] INFO org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@19dc67c2: startup date [Sun May 21 00:29:19 JST 2017]; root of context hierarchy
00:29:19.362 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Bean factory for org.springframework.context.support.GenericApplicationContext@19dc67c2: org.springframework.beans.factory.support.DefaultListableBeanFactory@d706f19: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,springTcfOnJunit5Test.LocalTestContext]; root of factory hierarchy
00:29:19.372 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
00:29:19.372 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
00:29:19.384 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' to allow for resolving potential circular references
00:29:19.387 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
00:29:19.467 [main] DEBUG org.springframework.context.annotation.ConfigurationClassEnhancer - Successfully enhanced com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext; enhanced class name is: com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext$$EnhancerBySpringCGLIB$$4923939c
00:29:19.468 [main] DEBUG org.springframework.context.annotation.ConfigurationClassPostProcessor - Replacing bean definition 'springTcfOnJunit5Test.LocalTestContext' existing class 'com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext' with enhanced class 'com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext$$EnhancerBySpringCGLIB$$4923939c'
00:29:19.471 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
00:29:19.471 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
00:29:19.472 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor' to allow for resolving potential circular references
00:29:19.496 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
00:29:19.496 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
00:29:19.496 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
00:29:19.496 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor' to allow for resolving potential circular references
00:29:19.500 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
00:29:19.500 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
00:29:19.500 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
00:29:19.505 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor' to allow for resolving potential circular references
00:29:19.509 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
00:29:19.511 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@79d8407f]
00:29:19.513 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@5aebe890]
00:29:19.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d706f19: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,springTcfOnJunit5Test.LocalTestContext]; root of factory hierarchy
00:29:19.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
00:29:19.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
00:29:19.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
00:29:19.514 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
00:29:19.515 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
00:29:19.515 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerProcessor'
00:29:19.521 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerProcessor' to allow for resolving potential circular references
00:29:19.522 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerProcessor'
00:29:19.523 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
00:29:19.523 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
00:29:19.523 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerFactory' to allow for resolving potential circular references
00:29:19.525 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
00:29:19.525 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'springTcfOnJunit5Test.LocalTestContext'
00:29:19.525 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'springTcfOnJunit5Test.LocalTestContext'
00:29:19.525 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'springTcfOnJunit5Test.LocalTestContext' to allow for resolving potential circular references
00:29:19.527 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'springTcfOnJunit5Test.LocalTestContext'
00:29:19.527 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
00:29:19.545 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@a1cdc6d]
00:29:19.545 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
00:29:19.547 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
00:29:19.548 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Storing ApplicationContext in cache under key [[MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
00:29:19.548 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@6236eb5f size = 1, maxSize = 32, parentContextCount = 0, hitCount = 0, missCount = 1]
00:29:19.552 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected element of bean 'com.example.spring5testdemo.SpringTcfOnJunit5Test': AutowiredFieldElement for private org.springframework.context.ApplicationContext com.example.spring5testdemo.SpringTcfOnJunit5Test.applicationContext
00:29:19.556 [main] DEBUG org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'com.example.spring5testdemo.SpringTcfOnJunit5Test' to bean named 'org.springframework.context.support.GenericApplicationContext@19dc67c2'
00:29:19.558 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test method: context [DefaultTestContext@69a10787 testClass = SpringTcfOnJunit5Test, testInstance = com.example.spring5testdemo.SpringTcfOnJunit5Test@11c20519, testMethod = defaultContextTest@SpringTcfOnJunit5Test, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].
00:29:19.559 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'messageSource'
00:29:19.560 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@69a10787 testClass = SpringTcfOnJunit5Test, testInstance = com.example.spring5testdemo.SpringTcfOnJunit5Test@11c20519, testMethod = defaultContextTest@SpringTcfOnJunit5Test, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].
00:29:19.561 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@69a10787 testClass = SpringTcfOnJunit5Test, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2d127a61 testClass = SpringTcfOnJunit5Test, locations = '{}', classes = '{class com.example.spring5testdemo.SpringTcfOnJunit5Test$LocalTestContext}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.459 sec - in com.example.spring5testdemo.SpringTcfOnJunit5Test
00:29:19.565 [Thread-1] INFO org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@19dc67c2: startup date [Sun May 21 00:29:19 JST 2017]; root of context hierarchy
00:29:19.566 [Thread-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
00:29:19.566 [Thread-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d706f19: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,springTcfOnJunit5Test.LocalTestContext]; root of factory hierarchy
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.369 s
[INFO] Finished at: 2017-05-21T00:29:19+09:00
[INFO] Final Memory: 19M/307M
[INFO] ------------------------------------------------------------------------
Note:
JUnit 5上で
MockMvc
を使用する場合は、実行結果を検証するコンポーネントがHamcrestのMatcher
の仕組みを利用しているため、hamcrest-core
を依存ライブラリに追加してください。pom.xml<dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> <scope>test</scope> </dependency>
@SpringJUnitConfig
を試す
@SpringJUnitConfig
は、Junit 5上でSpring TestContext Frameworkを利用することを示すための合成アノテーション(@ExtendWith(SpringExtension.class)
+ @ContextConfiguration
)で、必要に応じて@ContextConfiguration
が提供している属性を指定することができます。
package com.example.spring5testdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig // ★★★ @ExtendWith(SpringExtension.class)の代わりに指定
class SpringJUnitConfigOnJunit5Test {
@Autowired private ApplicationContext applicationContext;
@Test
void defaultContextTest() {
Assertions.assertEquals(1, applicationContext.getBeansOfType(MessageSource.class).size());
}
@Configuration
static class LocalTestContext {}
}
デフォルトの動作を変更したい場合は・・・
@SpringJUnitConfig(GlobalTestContext.class)
class SpringJUnitConfigOnJunit5Test {
// ...
}
といった感じで、@SpringJUnitConfig
の属性を使うことができます。
@SpringJUnitWebConfig
を試す
@SpringJUnitWebConfig
は、@SpringJUnitConfig
のWEB版で、@SpringJUnitConfig
との違いは@WebAppConfiguration
が付与されている/いないの違いだけです。
実際に@SpringJUnitWebConfig
を試すには、いくつか依存ライブラリを追加する必要があります。具体的には・・・・
- spring-web
- Servlet API 3.1+
です。このあたりはテスト対象のアプリケーションを作る or 動かす時に必要になるので普段はあまり気にしなくてもよい部分だとは思います。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
ライブラリを追加したら、@SpringJUnitWebConfig
を付与したテストケースクラスを作ります。
package com.example.spring5testdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.web.context.support.GenericWebApplicationContext;
@SpringJUnitWebConfig // ★★★ @ExtendWith(SpringExtension.class)の代わりに指定
class SpringJUnitWebConfigOnJunit5Test {
@Autowired private ApplicationContext applicationContext;
// Mockをインジェクションすることができる = @WebAppConfigurationが効いてる!
@Autowired private MockServletContext mockServletContext;
@Autowired private MockHttpSession mockHttpSession;
@Autowired private MockHttpServletRequest mockRequest;
@Test
void defaultContextTest() {
// ApplicationContextもWeb用のクラスが利用される = @WebAppConfigurationが効いてる!
Assertions.assertEquals(GenericWebApplicationContext.class, applicationContext.getClass());
Assertions.assertNotNull(mockServletContext);
Assertions.assertNotNull(mockHttpSession);
Assertions.assertNotNull(mockRequest);
Assertions.assertEquals(1, applicationContext.getBeansOfType(MessageSource.class).size());
}
@Configuration
static class LocalTestContext {}
}
@EnabledIf
と@DisabledIf
を試す
@EnabledIf
は「指定した条件を充す(SpELで指定したExpressionの結果がtrue
になる)場合にテストを実行すること」を、@DisabledIf
は「条件を充す時にテストをスキップすること」を示すアノテーションで、クラスレベルとメソッドレベルの両方に指定することができます。
本エントリーでは、OSがMacなら行うテストとスキップするテストがあった際に、このアノテーションを利用してテストの実行制御を行ってみます。
package com.example.spring5testdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.DisabledIf;
import org.springframework.test.context.junit.jupiter.EnabledIf;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig
class XxxIfOnJunit5Test {
@Test
@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
void enableOnMac() {
// OSがMacの時にテストが行われる
// ...
}
@Test
@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
void disableOnMac() {
// OSがMacの時はテストがスキップされる(=Mac以外の時はテストが行われる)
// ...
}
@Configuration
static class LocalTestContext {}
}
$ mvn test
...
Tests run: 2, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.128 sec - in com.example.spring5testdemo.XxxIfOnJunit5Test
...
特定のテストケースだけで使う条件(expression)なら上記のようにアドホックに記載してよいと思いますが、もし複数のテストで同じ条件を指定する必要があるなら、@EnabledIf
または@DisabledIf
をメタアノテーションにもつ合成アノテーションを作成する方がよいでしょう。
package com.example.spring5testdemo;
import org.springframework.test.context.junit.jupiter.EnabledIf;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnabledOnMac {
}
package com.example.spring5testdemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.test.context.junit.jupiter.DisabledIf;
@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisabledOnMac {
}
@Test
@EnabledOnMac // ★★★ 作成した合成アノテーションを指定して有効化の条件を共有
void enableOnMac() {
// ...
}
@Test
@DisabledOnMac // ★★★ 作成した合成アノテーションを指定してスキップ条件を共有
void disableOnMac() {
// ...
}
Spring TestContext Frameworkを使用したテストを平行実行できる
[SPR-5863] : Spring TestContext Framework(TCF)を使用したテストを平行実行できるようになります。 JUnit 5で試してみたかったのですが・・・平行実行を行う環境を「さく」っと作れなかったので、ここだけJUnit 4で試すことにしました・・・(悪しからず) なお、maven-surefire-pluginの依存ライブラリからJUnit 5用のライブラリを一時的に除外しておく必要があります。
Note:
本エントリーの中でも簡単に紹介しますが、平行実行時の制約や注意点については、「Spring Frameworkのリファレンス」をご覧ください。
<!-- ... -->
</dependencies>
<!-- ★★★ JUnit 4を追加 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<!-- ... -->
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<!-- ★★★ 平行実行の設定を追加(例:1JMV上で3スレッドでテストケースを実行する) -->
<configuration>
<parallel>methods</parallel>
<threadCount>3</threadCount>
</configuration>
</plugin>
</plugins>
</build>
<!-- ... -->
テストクラスに3つのテストメソッドを用意しておきます。
package com.example.spring5testdemo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@WebAppConfiguration
public class ParallelTest {
@Autowired private ApplicationContext ac;
@Autowired private MockHttpServletRequest request;
@Test
public void one() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
System.out.println("★★★★one★★★★" + Thread.currentThread().getName() + " :" + this);
System.out.println("★★★★one★★★★" + Thread.currentThread().getName() + " :" + ac);
System.out.println("★★★★one★★★★" + Thread.currentThread().getName() + " :" + ac.getBean(Foo.class));
System.out.println("★★★★one★★★★" + Thread.currentThread().getName() + " :" + request);
}
@Test
public void two() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
System.out.println("★★★★two★★★★" + Thread.currentThread().getName() + " :" + this);
System.out.println("★★★★two★★★★" + Thread.currentThread().getName() + " :" + ac);
System.out.println("★★★★two★★★★" + Thread.currentThread().getName() + " :" + ac.getBean(Foo.class));
}
@Test
public void three() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
System.out.println("★★★★three★★★★" + Thread.currentThread().getName() + " :" + this);
System.out.println("★★★★three★★★★" + Thread.currentThread().getName() + " :" + ac);
System.out.println("★★★★three★★★★" + Thread.currentThread().getName() + " :" + ac.getBean(Foo.class));
System.out.println("★★★★three★★★★" + Thread.currentThread().getName() + " :" + request);
}
@Configuration
static class LocalTestContext {
@Bean
Foo foo() {
return new Foo();
}
}
static class Foo {}
}
このテストケースを実行すると、以下のようなログ(抜粋)がコンソール上に出力されます。
$ mvn test -Dtest=ParallelTest
...
★★★★three★★★★pool-1-thread-3 :com.example.spring5testdemo.ParallelTest@7ff7cced
★★★★three★★★★pool-1-thread-3 :org.springframework.web.context.support.GenericWebApplicationContext@128169a9: startup date [Sun May 21 17:49:56 JST 2017]; root of context hierarchy
17:49:57.966 [pool-1-thread-3] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'foo'
★★★★three★★★★pool-1-thread-3 :com.example.spring5testdemo.ParallelTest$Foo@11a551ce
★★★★three★★★★pool-1-thread-3 :org.springframework.mock.web.MockHttpServletRequest@2695d62a
...
★★★★two★★★★pool-1-thread-2 :com.example.spring5testdemo.ParallelTest@1fad7c69
★★★★two★★★★pool-1-thread-2 :org.springframework.web.context.support.GenericWebApplicationContext@128169a9: startup date [Sun May 21 17:49:56 JST 2017]; root of context hierarchy
17:49:57.966 [pool-1-thread-2] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'foo'
★★★★two★★★★pool-1-thread-2 :com.example.spring5testdemo.ParallelTest$Foo@11a551ce
★★★★two★★★★pool-1-thread-2 :org.springframework.mock.web.MockHttpServletRequest@7927bef5
...
★★★★one★★★★pool-1-thread-1 :com.example.spring5testdemo.ParallelTest@3693321f
★★★★one★★★★pool-1-thread-1 :org.springframework.web.context.support.GenericWebApplicationContext@128169a9: startup date [Sun May 21 17:49:56 JST 2017]; root of context hierarchy
17:49:57.966 [pool-1-thread-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'foo'
★★★★one★★★★pool-1-thread-1 :com.example.spring5testdemo.ParallelTest$Foo@11a551ce
★★★★one★★★★pool-1-thread-1 :org.springframework.mock.web.MockHttpServletRequest@5950ee9d
...
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.013 sec - in com.example.spring5testdemo.ParallelTest
...
このログからわかることは・・・
- 3つのテストケースメソッドがそれぞれ異なるスレッド上で平行実行されている
- テストケースメソッド毎にテストケースクラスのインスタンスが生成されている
- インジェクションしている
ApplicationContext
は各テストケースで共有している - インジェクションしているMockオブジェクトは各テストケース毎にインスタンスが生成されている
ということです。
ポイントは・・・ApplicationContext
が共有されているという点でしょう。平行実行ではテストが同時に実行されるため、DIコンテナで管理しているオブジェクトの状態を更新するようなテストケースがあると、別のテストケースの実行結果に影響を与える可能性があります。また、TCFを使ったテストに限ったことではありませんが、外部リソース(データベース、ファイルなど)にアクセスするようなテストを平行実行すると、期待通りの実行結果にならないことがあります。
このように・・・平行実行できるか否か?は、アプリケーションやテストケースの作りに依存するので、安易に平行実行を取り入れると予期しないエラーが発生してしまうことがあるので注意が必要です。
テストの実行直前と実行直後に呼び出されるコールバックメソッドが追加される
[SPR-4365] : TestExecutionListener
インタフェースに、テストの実行直前と実行直後に呼び出されるコールバックメソッド(beforeTestExecution
とafterTestExecution
)が追加されます。
本エントリーではJUnit 5を使用して、「JUnit本体の仕組みで提供している前後処理」と「Spring TestContext Framework(TCF)の仕組みで提供している前後処理」の順序性を紹介します。
package com.example.spring5testdemo;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig
@TestExecutionListeners(TestExecutionListenerTest.MyTestExecutionListener.class) // ★★★ TestExecutionListenerの実装クラスを指定する
class TestExecutionListenerTest {
// ------------------------------------
// JUnit本体の仕組みで提供している前後処理
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
@BeforeAll
static void beforeAll(TestInfo testInfo) {
System.out.println("beforeAll");
}
@BeforeEach
void beforeEach(TestInfo testInfo) {
System.out.println(testInfo.getTestMethod().get().getName() + "-beforeEach");
}
@AfterEach
void afterEach(TestInfo testInfo) {
System.out.println(testInfo.getTestMethod().get().getName() + "-afterEach");
}
@AfterAll
static void afterAll(TestInfo testInfo) {
System.out.println("afterAll");
}
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// JUnit本体の仕組みで提供している前後処理
// ------------------------------------
// ------------
// テストメソッド
// ↓↓↓↓↓↓↓↓↓↓
@Test
void test1() {
System.out.println("test1");
}
@Test
void test2() {
System.out.println("test2");
}
// ↑↑↑↑↑↑↑↑↑↑
// テストメソッド
// ------------
// ------------------------------------------------------------
// Spring TestContext Framework(TCF)の仕組みで提供している前後処理
// ------------------------------------------------------------
static class MyTestExecutionListener implements TestExecutionListener {
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
System.out.println("beforeTestClass");
}
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
System.out.println("prepareTestInstance");
}
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
System.out.println(testContext.getTestMethod().getName() + "-beforeTestMethod");
}
@Override // ★★★ 5.0で追加されたメソッド
public void beforeTestExecution(TestContext testContext) throws Exception {
System.out.println(testContext.getTestMethod().getName() + "-beforeTestExecution");
}
@Override // ★★★ 5.0で追加されたメソッド
public void afterTestExecution(TestContext testContext) throws Exception {
System.out.println(testContext.getTestMethod().getName() + "-afterTestExecution");
}
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
System.out.println(testContext.getTestMethod().getName() + "-afterTestMethod");
}
@Override
public void afterTestClass(TestContext testContext) throws Exception {
System.out.println("afterTestClass");
}
}
@Configuration
static class LocalTestContext {}
}
上記のテストを実行すると、以下のようなコンソール出力になります。
12:01:15.250 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
12:01:15.250 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
12:01:15.250 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.example.spring5testdemo.TestExecutionListenerTest] from class [org.springframework.test.context.support.DefaultTestContextBootstrapper]
12:01:15.252 [main] DEBUG org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - Delegating to GenericXmlContextLoader to process context configuration [ContextConfigurationAttributes@4f80542f declaringClass = 'com.example.spring5testdemo.TestExecutionListenerTest', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
12:01:15.253 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.example.spring5testdemo.TestExecutionListenerTest]: class path resource [com/example/spring5testdemo/TestExecutionListenerTest-context.xml] does not exist
12:01:15.253 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.example.spring5testdemo.TestExecutionListenerTest]: no resource found for suffixes {-context.xml}.
12:01:15.253 [main] DEBUG org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - Delegating to AnnotationConfigContextLoader to process context configuration [ContextConfigurationAttributes@4f80542f declaringClass = 'com.example.spring5testdemo.TestExecutionListenerTest', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
12:01:15.254 [main] DEBUG org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Ignoring class [com.example.spring5testdemo.TestExecutionListenerTest$MyTestExecutionListener]; it must be static, non-private, non-final, and annotated with @Configuration to be considered a default configuration class.
12:01:15.254 [main] INFO org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - AnnotationConfigContextLoader detected default configuration classes for context configuration [ContextConfigurationAttributes@4f80542f declaringClass = 'com.example.spring5testdemo.TestExecutionListenerTest', classes = '{class com.example.spring5testdemo.TestExecutionListenerTest$LocalTestContext}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
12:01:15.255 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.example.spring5testdemo.TestExecutionListenerTest]
12:01:15.258 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [com.example.spring5testdemo.TestExecutionListenerTest$MyTestExecutionListener@130c12b7]
beforeTestClass
beforeAll
prepareTestInstance
test2-beforeTestMethod
test2-beforeEach
test2-beforeTestExecution ← 5.0で追加されたメソッド
test2
test2-afterTestExecution ← 5.0で追加されたメソッド
test2-afterEach
test2-afterTestMethod
prepareTestInstance
test1-beforeTestMethod
test1-beforeEach
test1-beforeTestExecution ← 5.0で追加されたメソッド
test1
test1-afterTestExecution ← 5.0で追加されたメソッド
test1-afterEach
test1-afterTestMethod
afterAll
afterTestClass
ちなみに・・・TCFがデフォルトで提供しているTestExecutionListener
の実装クラスと併用したい場合は、マージモードを指定する必要があります。(おそらく、併用したいケースの方が多いと思います)
@SpringJUnitConfig
@TestExecutionListeners(listeners = TestExecutionListenerTest.MyTestExecutionListener.class,
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) // mergeModeを変更(デフォのままだと置換されちゃう)
class TestExecutionListenerTest {
// ...
}
MockHttpServletRequest
にリクエストBODYを取得するメソッドが追加される
[SPR-14717] : MockHttpServletRequest
にリクエストBODYにアクセスするためのメソッド(getContentAsByteArray
とgetContentAsString
)が追加されます。この対応はこの後で説明する「MockMvc
使用時のテスト結果にリクエストBODYが出力される 」を対応するために行われたものです。正直・・・このメソッドを個別に使うことはほとんどないと思うので、サンプルを交えた説明は割愛させていただきます。
MockMvc
使用時のテスト結果にリクエストBODYが出力される
[SPR-14717] : MockMvc
使用時のテスト結果をコンソールまたはログに出力する際(MockMvcResultHandlers
のprint
またはlog
メソッドを使用する際)に、リクエストBODYも出力されるようになります。
具体的に「何が」「どのように」出力されるのかを見てみましょう。
っとその前に・・・MockMvc
を使うためにはspring-webmvcを依存ライブラリに追加する必要があります。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
依存ライブラリの追加が終わったら、テスト用のControllerとテストケースを作成します。
package com.example.spring5testdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.CharacterEncodingFilter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
class MvcMockTest {
private MockMvc mockMvc;
@BeforeEach
void setupMockMvc() {
mockMvc = MockMvcBuilders.standaloneSetup(new WelcomeRestController())
.addFilter(new CharacterEncodingFilter(StandardCharsets.UTF_8.name()))
.build();
}
@Test
void test() throws Exception {
mockMvc.perform(post("/post").contentType(MediaType.APPLICATION_FORM_URLENCODED)
.content("foo=bar") // ★★★ ← これが出力されるようになる
.param("foo", "baz")
).andExpect(
status().isOk()
).andExpect(
result -> Assertions.assertEquals("[baz, bar]", result.getResponse().getContentAsString())
// content().string("[baz, bar]") // Hamcrestがクラスパス上にある場合はこんな書き方ができる
).andDo(
print() // コンソール出力
).andDo(
log() // ログ出力
);
}
@RestController
static class WelcomeRestController {
@PostMapping("/post")
String post(@RequestParam List<String> foo) {
return foo.toString();
}
}
}
作成したテストを実行すると、コンソールやログファイルにテスト実行結果が出力されます。
MockHttpServletRequest:
HTTP Method = POST
Request URI = /post
Parameters = {foo=[baz, bar]}
Headers = {Content-Type=[application/x-www-form-urlencoded;charset=UTF-8]}
Body = foo=bar ★★★ ← 5.0からこれが出力される
Session Attrs = {}
Handler:
Type = com.example.spring5testdemo.MvcMockTest$WelcomeController
Method = java.lang.String com.example.spring5testdemo.MvcMockTest$WelcomeRestController.post(java.util.List<java.lang.String>)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[10]}
Content type = text/plain;charset=ISO-8859-1
Body = [baz, bar]
Forwarded URL = null
Redirected URL = null
Cookies = []
14:36:46.179 [main] DEBUG org.springframework.test.web.servlet.result - MvcResult details:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /post
Parameters = {foo=[baz, bar]}
Headers = {Content-Type=[application/x-www-form-urlencoded;charset=UTF-8]}
Body = foo=bar ★★★ ← 5.0からこれが出力される
Session Attrs = {}
Handler:
Type = com.example.spring5testdemo.MvcMockTest$WelcomeController
Method = java.lang.String com.example.spring5testdemo.MvcMockTest$WelcomeRestController.post(java.util.List<java.lang.String>)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[10]}
Content type = text/plain;charset=ISO-8859-1
Body = [baz, bar]
Forwarded URL = null
Redirected URL = null
Cookies = []
ちなみに・・・print()
やlog()
を全てのテストケースに適用したい場合は、AbstractMockMvcBuilder
に実装されているalwaysDo
メソッドを使いましょう。
@BeforeEach
void setupMockMvc() {
mockMvc = MockMvcBuilders.standaloneSetup(new WelcomeRestController())
.addFilter(new CharacterEncodingFilter(StandardCharsets.UTF_8.name()))
.alwaysDo(print()) // こちらに移動
.alwaysDo(log()) // こちらに移動
.build();
}
MockMvc
使用時の「リダイレクト先URL」と「フォワード先URL」の検証時にURIテンプレートが使える
[SPR-14790] : MockMvc
使用時の「リダイレクト先URL」と「フォワード先URL」を検証する際に、期待値にURIテンプレートを指定できるようになります。(個人的には「フォワード先URL」の方はあまり使う機会がない気がしています。)
package com.example.spring5testdemo;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.filter.CharacterEncodingFilter;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
class MvcMockTransitionTest {
private MockMvc mockMvc;
@BeforeEach
void setupMockMvc() {
mockMvc = MockMvcBuilders.standaloneSetup(new TransitionController())
.addFilter(new CharacterEncodingFilter(StandardCharsets.UTF_8.name()))
.build();
}
@Test
void testRedirect() throws Exception {
mockMvc.perform(get("/redirect/{id}", 1))
.andExpect(
status().isFound()
).andExpect(
redirectedUrl("/transition/redirect/{id}", 1) // ★★★ リダイレクト時のURL検証
);
}
@Test
void testForward() throws Exception {
mockMvc.perform(get("/forward/{id}", 1))
.andExpect(
status().isOk()
).andExpect(
forwardedUrl("/transition/forward/{id}", 1) // ★★★ フォワード時のURL検証
);
}
@Controller
static class TransitionController {
@GetMapping("/redirect/{id}")
String redirect(@PathVariable int id) {
return "redirect:/transition/redirect/{id}";
}
@GetMapping("/forward/{id}")
String forward(@PathVariable int id) {
return "forward:/transition/forward/" + id; // 個人的にこういうコードは基本的に書かないけど・・・(サンプルなので・・・)
}
}
}
XMLUnitのサポートバージョンが2.3になる
[SPR-14043] : XMLの検証をサポートするライブラリであるXMLUnitのサポートバージョンが2.3になります(本エントリーで紹介するサンプルコードは2.0.0でも動きました)。変更内容をみると2.x系からは検証用メソッド(assertXxx)のかわりにHamcrestのMatcher
が提供されるようになったようです。
Spring Testでは、MockMvc
使用時の処理結果を検証するコンポーネントの中でXMLUnitに依存しているものがあります。
XMLUnitに依存している機能を利用する場合は、以下のようにXMLUnitを依存ライブラリに加える必要があります。
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
package com.example.spring5testdemo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.CharacterEncodingFilter;
import java.nio.charset.StandardCharsets;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
class XmlunitTest {
private MockMvc mockMvc;
@BeforeEach
void setupMockMvc() {
mockMvc = MockMvcBuilders.standaloneSetup(new XmlRestController())
.addFilter(new CharacterEncodingFilter(StandardCharsets.UTF_8.name()))
.alwaysDo(log())
.build();
}
@Test
void test() throws Exception {
mockMvc.perform(get("/get"))
.andExpect(
status().isOk()
).andExpect(
content().xml("<root><a>test</a></root>") // ★★★ XML文書として評価する(=スペースや改行の違いは差分として扱われない)
);
}
@RestController
static class XmlRestController {
@GetMapping("/get")
String get() {
return "<root> <a>test</a> </root>";
}
}
}
Note:
実際の検証処理は
XmlExpectationsHelper
に委譲しているので、MockMvc
以外のところでも同等の検証を行うことが可能です。
まとめ
今回は、Test関連の主な変更点を紹介しました。やはり・・JUnit 5のサポートが最大の目玉ですね。JUnit 5はJUnit 4のことをJUnit Vintage(= Not後ろ向きな表現)としているところが個人的には好感がもてますw 名前の話だけではなく、「JUnit 5で書かれたテストケース」と「JUnit 4で書かれたテストケース」を同時に実行できるのも魅力的です(過去の成果物をそのまま移行できる!!=余力ができたらコードレベルでJUnit 5化すればいい!!)。Maven(maven-surefire-plugin)なら・・・
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit-platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
</dependency>
<!-- ★★★ ↓ これを追加すればJUnit 5とJUnit 4で書かれたテストケースを同時にテストできる -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>4.12.0-M4</version>
</dependency>
</dependencies>
</plugin>
ってな感じにすればOKです。
Spring Frameworkの話から脱線してしまいましたが・・・ アプリケーション開発を行う上でテストは非常に大事な要素であり、Spring Frameworkもテストコードを作るための支援機能を豊富に提供してくれています。(目先のスケジュールを守るために)「時間がないからテストは後回し(=動作確認をテストと言い張る)!」「(打鍵の)結合試験でやるから単体テストしない!」とか説得力0な事を言う人がいますが、最終的に誰も得しないので、スケジュールを見直して(調整して)テストコードをちゃんと作るようにしましょう。
Springを使ってアプリケーションを作る場合は、「Spring Frameworkのリファレンス(テスト編)」を一読することをオススメします。(けっこう量ありますがw)
次回は、Reactive Programing ModelのWebフレームワーク(WebFlux)を紹介する予定です。
付録:Spring Boot 2.0でJUnit 5を使う方法
ここでは、Spring Boot 2.0でJUnit 5(+JUnit 4)を使う(共存する)方法を紹介します。基本的には本エントリーで説明したことをSpring Bootアプリケーションに適用するだけです。
- Spring Bootプロジェクトの作成
$ curl -s https://start.spring.io/starter.tgz\
-d name=test-demo\
-d artifactId=test-demo\
-d baseDir=test-demo\
-d bootVersion=2.0.0.M1\
| tar -xzvf -
- pom.xmlを修正
<?xml version="1.0" encoding="UTF-8"?>
<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>test-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>test-demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<junit-jupiter.version>5.0.0-M4</junit-jupiter.version> <!-- ★★★ Add ★★★ -->
<junit-platform.version>1.0.0-M4</junit-platform.version> <!-- ★★★ Add ★★★ -->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ★★★ Add start ★★★ -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- ★★★ Add end ★★★ -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- ★★★ Add start ★★★ -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit-platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.version}.0-M4</version>
</dependency>
</dependencies>
</plugin>
<!-- ★★★ Add end ★★★ -->
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
- JUnit 5用のテストケースクラス(空のテストケース)の作成
package com.example.testdemo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@SpringBootTest
class TestDemoApplicationUsingJUnit5Tests {
@Test
void contextLoads() {
}
}
- テストの実行
$ ./mvnw test
/usr/local/apps/test-demo
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building test-demo 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ test-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ test-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ test-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /usr/local/apps/test-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ test-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.19.1:test (default-test) @ test-demo ---
-------------------------------------------------------
T E S T S
-------------------------------------------------------
19:23:22.969 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class com.example.testdemo.TestDemoApplicationTests]
19:23:22.973 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
19:23:22.978 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
19:23:22.990 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.example.testdemo.TestDemoApplicationTests] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
19:23:22.999 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.example.testdemo.TestDemoApplicationTests], using SpringBootContextLoader
19:23:23.002 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.example.testdemo.TestDemoApplicationTests]: class path resource [com/example/testdemo/TestDemoApplicationTests-context.xml] does not exist
19:23:23.002 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.example.testdemo.TestDemoApplicationTests]: class path resource [com/example/testdemo/TestDemoApplicationTestsContext.groovy] does not exist
19:23:23.002 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.example.testdemo.TestDemoApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}.
19:23:23.003 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.example.testdemo.TestDemoApplicationTests]: TestDemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
19:23:23.049 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.055 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
19:23:23.055 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
19:23:23.056 [main] DEBUG org.springframework.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
19:23:23.066 [main] DEBUG org.springframework.core.io.support.PathMatchingResourcePatternResolver - Resolved classpath location [com/example/testdemo/] to resources [URL [file:/usr/local/apps/test-demo/target/test-classes/com/example/testdemo/], URL [file:/usr/local/apps/test-demo/target/classes/com/example/testdemo/]]
19:23:23.067 [main] DEBUG org.springframework.core.io.support.PathMatchingResourcePatternResolver - Looking for matching resources in directory tree [/usr/local/apps/test-demo/target/test-classes/com/example/testdemo]
19:23:23.067 [main] DEBUG org.springframework.core.io.support.PathMatchingResourcePatternResolver - Searching directory [/usr/local/apps/test-demo/target/test-classes/com/example/testdemo] for files matching pattern [/usr/local/apps/test-demo/target/test-classes/com/example/testdemo/*.class]
19:23:23.069 [main] DEBUG org.springframework.core.io.support.PathMatchingResourcePatternResolver - Looking for matching resources in directory tree [/usr/local/apps/test-demo/target/classes/com/example/testdemo]
19:23:23.069 [main] DEBUG org.springframework.core.io.support.PathMatchingResourcePatternResolver - Searching directory [/usr/local/apps/test-demo/target/classes/com/example/testdemo] for files matching pattern [/usr/local/apps/test-demo/target/classes/com/example/testdemo/*.class]
19:23:23.069 [main] DEBUG org.springframework.core.io.support.PathMatchingResourcePatternResolver - Resolved location pattern [classpath*:com/example/testdemo/*.class] to resources [file [/usr/local/apps/test-demo/target/test-classes/com/example/testdemo/TestDemoApplicationTests.class], file [/usr/local/apps/test-demo/target/test-classes/com/example/testdemo/TestDemoApplicationUsingJUnit5Tests.class], file [/usr/local/apps/test-demo/target/classes/com/example/testdemo/TestDemoApplication.class]]
19:23:23.113 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/usr/local/apps/test-demo/target/classes/com/example/testdemo/TestDemoApplication.class]
19:23:23.114 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.example.testdemo.TestDemoApplication for test class com.example.testdemo.TestDemoApplicationTests
19:23:23.203 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.example.testdemo.TestDemoApplicationTests]: using defaults.
19:23:23.203 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
19:23:23.209 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
19:23:23.209 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
19:23:23.212 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
19:23:23.212 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@d35dea7, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@7770f470, org.springframework.test.context.support.DirtiesContextTestExecutionListener@5e5d171f, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@24313fcc, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@7d20d0b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@77f1baf5, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@41a2befb, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@6c40365c, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@7bedc48a]
19:23:23.214 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.214 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.example.testdemo.TestDemoApplicationTests]
Running com.example.testdemo.TestDemoApplicationTests
19:23:23.244 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class com.example.testdemo.TestDemoApplicationTests]
19:23:23.245 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
19:23:23.245 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
19:23:23.245 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.example.testdemo.TestDemoApplicationTests] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
19:23:23.245 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.example.testdemo.TestDemoApplicationTests], using SpringBootContextLoader
19:23:23.246 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.example.testdemo.TestDemoApplicationTests]: class path resource [com/example/testdemo/TestDemoApplicationTests-context.xml] does not exist
19:23:23.246 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.example.testdemo.TestDemoApplicationTests]: class path resource [com/example/testdemo/TestDemoApplicationTestsContext.groovy] does not exist
19:23:23.246 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.example.testdemo.TestDemoApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}.
19:23:23.246 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.example.testdemo.TestDemoApplicationTests]: TestDemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
19:23:23.249 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.249 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
19:23:23.250 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
19:23:23.250 [main] DEBUG org.springframework.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
19:23:23.251 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.example.testdemo.TestDemoApplication for test class com.example.testdemo.TestDemoApplicationTests
19:23:23.258 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.example.testdemo.TestDemoApplicationTests]: using defaults.
19:23:23.258 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
19:23:23.258 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
19:23:23.259 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
19:23:23.260 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
19:23:23.260 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@50caa560, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@2a266d09, org.springframework.test.context.support.DirtiesContextTestExecutionListener@5ab9e72c, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@186f8716, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@1d8bd0de, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@45ca843, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@11c9af63, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@757acd7b, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@36b4fe2a]
19:23:23.260 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.260 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.294 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.294 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.295 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.295 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.296 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.296 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.299 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@693fe6c9 testClass = TestDemoApplicationTests, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@34f5090e testClass = TestDemoApplicationTests, locations = '{}', classes = '{class com.example.testdemo.TestDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@25359ed8, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@80ec1f8, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@709ba3fb], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].
19:23:23.299 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.299 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.example.testdemo.TestDemoApplicationTests]
19:23:23.300 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@693fe6c9 testClass = TestDemoApplicationTests, testInstance = com.example.testdemo.TestDemoApplicationTests@22e357dc, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@34f5090e testClass = TestDemoApplicationTests, locations = '{}', classes = '{class com.example.testdemo.TestDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@25359ed8, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@80ec1f8, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@709ba3fb], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]].
19:23:23.315 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
19:23:23.316 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
19:23:23.316 [main] DEBUG org.springframework.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
19:23:23.320 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=-1}
19:23:23.320 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [Inlined Test Properties] PropertySource with highest search precedence
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.0.M1)
2017-05-21 19:23:23.786 INFO 70579 --- [ main] c.e.testdemo.TestDemoApplicationTests : Starting TestDemoApplicationTests on Kazuki-no-MacBook-Pro.local with PID 70579 (started by shimizukazuki in /usr/local/apps/test-demo)
2017-05-21 19:23:23.788 INFO 70579 --- [ main] c.e.testdemo.TestDemoApplicationTests : No active profile set, falling back to default profiles: default
2017-05-21 19:23:23.807 INFO 70579 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@387a8303: startup date [Sun May 21 19:23:23 JST 2017]; root of context hierarchy
2017-05-21 19:23:24.135 INFO 70579 --- [ main] c.e.testdemo.TestDemoApplicationTests : Started TestDemoApplicationTests in 0.81 seconds (JVM running for 1.56)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.973 sec - in com.example.testdemo.TestDemoApplicationTests
Running com.example.testdemo.TestDemoApplicationUsingJUnit5Tests
2017-05-21 19:23:24.242 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.example.testdemo.TestDemoApplicationUsingJUnit5Tests], using SpringBootContextLoader
2017-05-21 19:23:24.242 INFO 70579 --- [ main] o.s.t.c.support.AbstractContextLoader : Could not detect default resource locations for test class [com.example.testdemo.TestDemoApplicationUsingJUnit5Tests]: no resource found for suffixes {-context.xml, Context.groovy}.
2017-05-21 19:23:24.242 INFO 70579 --- [ main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.example.testdemo.TestDemoApplicationUsingJUnit5Tests]: TestDemoApplicationUsingJUnit5Tests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2017-05-21 19:23:24.244 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.example.testdemo.TestDemoApplication for test class com.example.testdemo.TestDemoApplicationUsingJUnit5Tests
2017-05-21 19:23:24.247 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
2017-05-21 19:23:24.247 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
2017-05-21 19:23:24.247 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
2017-05-21 19:23:24.247 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
2017-05-21 19:23:24.248 INFO 70579 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@665e9289, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@7d3430a7, org.springframework.test.context.support.DirtiesContextTestExecutionListener@6f603e89, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@2756c0a7, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@350ec41e, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@69637b10, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@71984c3, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@165b2f7f, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@5536379e]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 sec - in com.example.testdemo.TestDemoApplicationUsingJUnit5Tests
2017-05-21 19:23:24.258 INFO 70579 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@387a8303: startup date [Sun May 21 19:23:23 JST 2017]; root of context hierarchy
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.126 s
[INFO] Finished at: 2017-05-21T19:23:24+09:00
[INFO] Final Memory: 20M/347M
[INFO] ------------------------------------------------------------------------