LoginSignup
21
20

More than 3 years have passed since last update.

ユニットテスト環境にJUnit5とAssertJ、Mockitoを使ったプロジェクトのひな型のメモ

Last updated at Posted at 2018-12-30

概要

この記事は、ユニットテスト環境にJUnit5、AssertJ、Mockitoを使ったプロジェクトのひな型を作った時のメモです。

環境

  • Windows 10 Professional
  • OpenJDK 11
  • JUnit5 5.3.2
  • AssertJ 3.11.1
  • Mockito 2.23.4
  • Maven 3.6.0
  • Eclipse 2018-09

参考

JDK 11

> java -version
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)

Maven

Mavenは2018/10/24にリリースされた3.6.0を利用しました。
Release Notes – Maven 3.6.0

D:\dev\apache-maven-3.6.0> bin\mvn.cmd -v
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T03:41:47+09:00)
Maven home: D:\dev\apache-maven-3.6.0\bin\..
Java version: 11.0.1, vendor: Oracle Corporation, runtime: D:\openjdk\openjdk-11.0.1_windows-x64
Default locale: ja_JP, platform encoding: MS932
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

pom.xml

最終的なpom.xmlの内容を掲載します。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>junit5-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>junit5-demo</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>11</java.version>
    <!-- compiler -->
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
    <maven.compiler.showWarnings>true</maven.compiler.showWarnings>
    <maven.compiler.verbose>true</maven.compiler.verbose>
    <!-- JUnit5 -->
    <junit.jupiter.version>5.3.2</junit.jupiter.version>
    <junit.platform.version>1.3.2</junit.platform.version>
    <!-- AssertJ -->
    <assertj.version>3.11.1</assertj.version>
    <!-- Mockito -->
    <mockito.version>2.23.4</mockito.version>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.jupiter.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>${junit.jupiter.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher -->
    <dependency>
      <groupId>org.junit.platform</groupId>
      <artifactId>junit-platform-launcher</artifactId>
      <version>${junit.platform.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>${assertj.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>${mockito.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-junit-jupiter</artifactId>
      <version>${mockito.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>demo</finalName>
    <plugins>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-clean-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <compilerArgs>
            <arg>-Xlint:all</arg>
          </compilerArgs>
          <release>${java.version}</release>
        </configuration>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-resources-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-jar-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.1.1</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-surefire-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M3</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-failsafe-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>3.0.0-M3</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-dependency-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>3.1.1</version>
        <executions>
          <execution>
            <id>tree</id>
            <phase>package</phase>
            <goals>
              <goal>tree</goal>
            </goals>
            <configuration>
              <outputFile>dependency.txt</outputFile>
              <outputType>text</outputType>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-site-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.7.1</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/com.github.spotbugs/spotbugs-maven-plugin -->
      <plugin>
        <groupId>com.github.spotbugs</groupId>
        <artifactId>spotbugs-maven-plugin</artifactId>
        <version>3.1.10</version>
        <configuration>
          <xmlOutput>true</xmlOutput>
          <!-- Optional directory to put spotbugs xdoc xml report -->
          <xmlOutputDirectory>target/site</xmlOutputDirectory>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <reporting>
    <plugins>
      <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-project-info-reports-plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-project-info-reports-plugin</artifactId>
        <version>3.0.0</version>
      </plugin>
      <!-- https://mvnrepository.com/artifact/com.github.spotbugs/spotbugs-maven-plugin -->
      <plugin>
        <groupId>com.github.spotbugs</groupId>
        <artifactId>spotbugs-maven-plugin</artifactId>
        <version>3.1.10</version>
      </plugin>
    </plugins>
  </reporting>

</project>

Maven Plugins

よく利用するプラグインについては、バージョン3.0以上の現時点(2018/12)で最新のバージョンを指定しました。
Available Plugins

Maven Clean Plugin

Release Notes – Maven Clean Plugin – Version 3.1.0

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-clean-plugin -->
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-clean-plugin</artifactId>
    <version>3.1.0</version>
</dependency>

Maven Compiler Plugin

バージョン3.8.0でJDK 11に関するバグが修正されています。(Unsupported class file major version 55)
Release Notes – Maven Compiler Plugin – Version 3.8.0

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
</dependency>

Maven Resources Plugin

Release Notes – Apache Maven Resources Version 3.1.0

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-resources-plugin -->
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.1.0</version>
</dependency>

Maven JAR Plugin

Release Notes – Maven JAR Plugin – Version 3.1.0

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-jar-plugin -->
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.1.1</version>
</dependency>

Maven Surefire Plugin

Using JUnit 5 Platform

バージョン2.22.1からJDK 11をサポートしていますが、今回は3.0.0-M3を利用することにしました。

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-surefire-plugin -->
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M3</version>
</dependency>

Maven Failsafe Plugin

FailsafeのバージョンもSurefireに合わせました。

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-failsafe-plugin -->
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>3.0.0-M3</version>
    <scope>test</scope>
</dependency>

JUnit5

junit-jupiter-engine

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.3.2</version>
    <scope>test</scope>
</dependency>

junit-jupiter-api

junit-jupiter-engineを依存関係に組み込むと推移的依存関係としてjunit-jupiter-apiが暗黙的に組み込まれるため、junit-jupiter-apiをpom.xmlに明示的に宣言する必要はありません。

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.3.2</version>
    <scope>test</scope>
</dependency>

junit-jupiter-params

junit-jupiter-paramsはパラメータ化テスト(Parameterized Tests)が必要な場合に追加するオプショナル機能です。

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.3.2</version>
    <scope>test</scope>
</dependency>     

junit-platform-launcher

<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.3.2</version>
    <scope>test</scope>
</dependency>

junit-platform-surefire-provider

MavenからJUnit5のテストを実行するSurefireプラグインが組み込まれています。
ただし、下記に引用した通りJUnit Platform 1.4で削除される予定です。

Maven

The custom junit-platform-surefire-provider, which was originally developed by the JUnit team, has been deprecated and is scheduled to be removed in JUnit Platform 1.4. Please use Maven Surefire’s native support instead.

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-surefire-plugin -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.19.1</version>
  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-surefire-provider -->
    <dependency>
      <groupId>org.junit.platform</groupId>
      <artifactId>junit-platform-surefire-provider</artifactId>
      <version>1.3.2</version>
    </dependency>
  </dependencies>
</plugin>

Maven Surefire version 2.22.0以上を利用する場合は、JUnit5をネイティブサポートするので、junit-platform-surefire-providerは不要です。
今回は3.0.0-M3を利用することにしたので使いませんでした。

<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-surefire-plugin -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.1</version>
</plugin>

AssertJ

JDK 11やJUnit5との組み合わせで特に気を付けないといけない点はありませんでした。

assertj-core

<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.11.1</version>
    <scope>test</scope>
</dependency>

Mockito

Mockitoを利用するために必要な依存関係は下記の通りです。
JUnit5環境で利用する場合はmockito-junit-jupiterが必要になります。

mockito-core

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.23.4</version>
    <scope>test</scope>
</dependency>

mockito-junit-jupiter

MockitoExtensionクラス、MockitoSettingsアノテーションが利用できるようになります。

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>2.23.4</version>
    <scope>test</scope>
</dependency>

テストクラスのひな型

package com.example.demo;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.LocalDateTime;
import java.util.List;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestReporter;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.example.demo.service.UserService;

// JUnit5のMockito拡張を有効にする
@ExtendWith(MockitoExtension.class)
@DisplayName("JUnit5 Test Demo")
public class JUnit5DemoTests {

    @Mock(answer = Answers.RETURNS_SMART_NULLS)
    UserService userService;

    // テストメソッドの前に実行される
    @BeforeEach
    // TestInfoを使用して、テストの表示名、テストクラス、テストメソッド、関連付けられているタグなど、現在のテストに関する情報を取得できます
    void setup(TestInfo testInfo) {
        System.out.println("setup: " + testInfo.getDisplayName());
    }

    // テストメソッドの後に実行される
    @AfterEach
    void tearDown() {
        System.out.println("tearDown");
    }

    @Test
    void userItems() {
        when(userService.getItems(eq(100L))).thenReturn(List.of("Apple", "Banana", "Cherry"));

        List<String> items = userService.getItems(100L);
        assertThat(items).hasSize(3);

        verify(userService).getItems(eq(100L));
    }

    @Test
    @DisplayName("demo1をテストする")
    // TestReporterを使用して現在のテスト実行に関する追加データを公開することができます
    // データはIDEによって表示されるかレポートに含まれるようになります
    void demo1(TestReporter reporter) {
        reporter.publishEntry("demo1のテスト");
        // demo1のテスト
        reporter.publishEntry("demo1 key", "demo1 value");
    }

    @Nested
    @DisplayName("Hoge Tests")
    class HogeTests {

        @RepeatedTest(value = 3, name = RepeatedTest.LONG_DISPLAY_NAME)
        @DisplayName("hogeをテストする")
        // RepetitionInfoを使用して、現在の繰り返しおよび対応する@RepeatedTestの繰り返しの総数に関する情報を取得できます
        void hoge(RepetitionInfo info) {
            System.out.println("hoge current:" + info.getCurrentRepetition() + " total:" + info.getTotalRepetitions());
        }

    }

    @Nested
    @DisplayName("Fuga Tests")
    class FugaTests {

        @ParameterizedTest
        @CsvSource({ "foo, 10000, 100, 2018-12-30T00:00:00.000", "bar, 20000, 200, 2018-12-30T12:00:00.000",
                "baz, 30000, 300, 2018-12-30T23:00:00.000" })
        @DisplayName("fugaをテストする")
        void fuga(String first, int second, long third, LocalDateTime fourth) {
            System.out.println(
                    "fuga first:" + first + " second:" + second + " third:" + third + " fourth:" + fourth.toString());
        }

    }

}

junit5.png

JUnit5

Annotations

JUnit5 @Target JUnit4's
@Test ANNOTATION_TYPE
METHOD
@Test
@ParameterizedTest ANNOTATION_TYPE
METHOD
@RepeatedTest ANNOTATION_TYPE
METHOD
@TestFactory ANNOTATION_TYPE
METHOD
@TestInstance TYPE
@TestTemplate ANNOTATION_TYPE
METHOD
@DisplayName TYPE
METHOD
@BeforeEach ANNOTATION_TYPE
METHOD
@Before
@AfterEach ANNOTATION_TYPE
METHOD
@After
@BeforeAll ANNOTATION_TYPE
METHOD
@BeforeClass
@AfterAll ANNOTATION_TYPE
METHOD
@AfterClass
@Nested TYPE
@Tag TYPE
METHOD
@Disabled TYPE
METHOD
@Ignore
@ExtendWith TYPE
METHOD

Test Classes and Methods

A test method is any instance method that is directly or meta-annotated with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, or @TestTemplate.

@Test

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@Testable
public @interface Test {
}

@TestTemplate

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@Testable
public @interface TestTemplate {
}

@TestTemplate

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@Testable
public @interface TestTemplate {
}

@Testable

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@API(status = STABLE, since = "1.0")
public @interface Testable {
}

Repeated Tests

@RepeatedTest

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@TestTemplate
public @interface RepeatedTest {
//...
}

Parameterized Tests

@ParameterizedTest

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = EXPERIMENTAL, since = "5.0")
@TestTemplate
@ExtendWith(ParameterizedTestExtension.class)
public @interface ParameterizedTest {
//...
}

パラメータソースに指定に使用するアノテーション

Annotation Source
@ValueSource 配列
@EnumSource Enum
@MethodSource テストクラス、外部クラスのファクトリメソッド
@CsvSource カンマ区切りの文字列
@CsvFileSource クラスパス上のCSVファイル
@ArgumentsSource ArgumentsProviderの実装クラス

Extension Model

In contrast to the competing Runner, @Rule, and @ClassRule extension points in JUnit 4, the JUnit Jupiter extension model consists of a single, coherent concept: the Extension API. Note, however, that Extension itself is just a marker interface.

@ExtendWith

@ExtendWithで宣言的(declaratively)に拡張機能を登録します。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Repeatable(Extensions.class)
@API(status = STABLE, since = "5.0")
public @interface ExtendWith {
//...省略
}

@RegisterExtension

@RegisterExtensionでプログラム的(programmatically)に拡張機能を登録します。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.1")
public @interface RegisterExtension {
}

Mockito

下記のようにstaticインポートしておくと便利です。

import static org.mockito.Mockito.*;

必要であればMatchersもstaticインポートしておきます。

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.AdditionalMatchers.*;

MockitoExtension

Extension that initializes mocks and handles strict stubbings. This extension is the JUnit Jupiter equivalent of our JUnit4 MockitoJUnitRunner.

@ExtendWith(MockitoExtension.class)

MockitoSettings

Annotation that can configure Mockito as invoked by the MockitoExtension.

@MockitoSettings

Strictness

Strictness behavior
LENIENT Mockito 1.xと同じ動作.
WARN Mockito 2.xと同じ動作.
STRICT_STUBS Default. 計画ではMockito 3.xのデフォルトの動作の予定.

AssertJ

JUnit5環境でのAssertJの書き方に特別変わった点はないようなので省略します。

// entry point for all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;
21
20
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
21
20