LoginSignup
41
46

More than 5 years have passed since last update.

Spring Framework 5.0 Test関連の主な変更点

Last updated at Posted at 2017-05-21

今回は「Spring Framework 5.0 主な変更点」シリーズの第5回で、Test関連の主な変更点(新機能や改善点など)を紹介していきたいと思います。

シリーズ

動作検証バージョン

  • 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上で利用できるようになります。 [詳細へ:arrow_right:]

Note: SpringExtensionクラス、@SpringJUnitConfigアノテーション、@SpringJUnitWebConfigアノテーション、@EnabledIfアノテーション、@DisabledIfアノテーションが追加されます。
2 Spring TestContext Frameworkを使用したテストを平行実行できるようになります。 [詳細へ:arrow_right:]
3 TestExecutionListenerインタフェースに、テストの実行直前と実行直後に呼び出されるコールバックメソッド(beforeTestExecutionafterTestExecution)が追加されます。 [詳細へ:arrow_right:]
4 MockHttpServletRequestにリクエストBODYにアクセスするためのメソッド(getContentAsByteArraygetContentAsString)が追加されます。 [詳細へ:arrow_right:]
5 MockMvc使用時のテスト結果をコンソールまたはログに出力する際(MockMvcResultHandlersprintまたはlogメソッドを使用する際)に、リクエストBODYも出力されるようになります。 [詳細へ:arrow_right:]
6 MockMvc使用時の「リダイレクト先URL」と「フォワード先URL」を検証する際に、期待値にURIテンプレートを指定できるようになります。 [詳細へ:arrow_right:]
7 XMLの検証をサポートするライブラリであるXMLUnitのサポートバージョンが2.3になります。 [詳細へ:arrow_right:]

JUnit 5がサポートされる :thumbsup:

[SPR-13575] : Spring Framework 5.0におけるテスト関連の変更点の目玉は、なんといってもJUnit 5 Jupiter上でSpring TestContext Framework(TCF)が利用できるように対応されることでしょう。具体的には・・・

クラス(アノテーション) 説明
SpringExtension Junit 5が提供している「拡張ポイント(Extension)」を利用して、Junit 5上でSpring TestContext Frameworkを使えるようにしているクラスです。
JUnit 4上ではSpringRunnerSpringClassRuleSpringMethodRuleが同じ役割を担っています。
@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でテストを動かしてみよう :punch:

本エントリーでは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を作成します。

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を試す :point_right:

JUnit 5上でSpring TestContext Frameworkを利用する場合は、SpringExtensionの指定が必要になりますが、まずSpring Testを使えるようにするために、pom.xmlを以下のように修正する必要があります。

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を試す :point_right:

@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 {}

}

デフォルトの動作を変更したい場合は・・・

テスト向けに用意されたJavaConfigクラスを使う場合
@SpringJUnitConfig(GlobalTestContext.class)
class SpringJUnitConfigOnJunit5Test {
    // ...
}

といった感じで、@SpringJUnitConfigの属性を使うことができます。

@SpringJUnitWebConfigを試す :point_right:

@SpringJUnitWebConfigは、@SpringJUnitConfigのWEB版で、@SpringJUnitConfigとの違いは@WebAppConfigurationが付与されている/いないの違いだけです。

実際に@SpringJUnitWebConfigを試すには、いくつか依存ライブラリを追加する必要があります。具体的には・・・・

  • spring-web
  • Servlet API 3.1+

です。このあたりはテスト対象のアプリケーションを作る or 動かす時に必要になるので普段はあまり気にしなくてもよい部分だとは思います。

pom.xml
<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を試す :point_right:

@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をメタアノテーションにもつ合成アノテーションを作成する方がよいでしょう。

Macの時にテストを有効化するためのアノテーション
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 {
}
Macの時にテストをスキップするためのアノテーション
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を使用したテストを平行実行できる :thumbsup:

[SPR-5863] : Spring TestContext Framework(TCF)を使用したテストを平行実行できるようになります。 JUnit 5で試してみたかったのですが・・・平行実行を行う環境を「さく」っと作れなかったので、ここだけJUnit 4で試すことにしました・・・:sweat_smile:(悪しからず) なお、maven-surefire-pluginの依存ライブラリからJUnit 5用のライブラリを一時的に除外しておく必要があります。

Note:

本エントリーの中でも簡単に紹介しますが、平行実行時の制約や注意点については、「Spring Frameworkのリファレンス」をご覧ください。

pom.xml
<!-- ... -->
</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を使ったテストに限ったことではありませんが、外部リソース(データベース、ファイルなど)にアクセスするようなテストを平行実行すると、期待通りの実行結果にならないことがあります。
このように・・・平行実行できるか否か?は、アプリケーションやテストケースの作りに依存するので、安易に平行実行を取り入れると予期しないエラーが発生してしまうことがあるので注意が必要です。

テストの実行直前と実行直後に呼び出されるコールバックメソッドが追加される :thumbsup:

[SPR-4365] : TestExecutionListenerインタフェースに、テストの実行直前と実行直後に呼び出されるコールバックメソッド(beforeTestExecutionafterTestExecution)が追加されます。

本エントリーでは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を取得するメソッドが追加される :thumbsup:

[SPR-14717] : MockHttpServletRequestにリクエストBODYにアクセスするためのメソッド(getContentAsByteArraygetContentAsString)が追加されます。この対応はこの後で説明する「MockMvc使用時のテスト結果にリクエストBODYが出力される :thumbsup:」を対応するために行われたものです。正直・・・このメソッドを個別に使うことはほとんどないと思うので、サンプルを交えた説明は割愛させていただきます。

MockMvc使用時のテスト結果にリクエストBODYが出力される :thumbsup:

[SPR-14717] : MockMvc使用時のテスト結果をコンソールまたはログに出力する際(MockMvcResultHandlersprintまたはlogメソッドを使用する際)に、リクエストBODYも出力されるようになります。

具体的に「何が」「どのように」出力されるのかを見てみましょう。
っとその前に・・・MockMvcを使うためにはspring-webmvcを依存ライブラリに追加する必要があります。

pom.xml
<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();
        }
    }

}

作成したテストを実行すると、コンソールやログファイルにテスト実行結果が出力されます。

print()の出力結果(コンソール)
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 = []
log()の出力結果(コンソールやログファイルなど)
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テンプレートが使える :thumbsup:

[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を依存ライブラリに加える必要があります。

pom.xml
<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)なら・・・

pom.xml
<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の話から脱線してしまいましたが:sweat_smile:・・・ アプリケーション開発を行う上でテストは非常に大事な要素であり、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を修正
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] ------------------------------------------------------------------------
41
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
46