Java
2022.04.21
JUnitとは?基本的な使い方からユニットテストのやり方までわかりやすく解説
2023.11.10

開発業務に携わる上で知っておきたいのが「テスト」です。


Javaでは、単体テストに「JUnit」と呼ばれるテストツールを利用して実施することがほとんどです。


本記事では、JUnitを使った単体テストをする場合に必要な知識や手順を詳しく解説していきます。


1.JUnitとは?

JUnitは、Javaで単体テストを実施するためのテスティングフレームワークです。


Javaに限らず、各言語に対してこのような単体テストのテスティングフレームワークが存在しており、総称して「xUnit」と呼ばれています。


JUnitは、Javaに対応したxUnitであることから「JUnit」と呼ばれています。

①そもそも単体テストとは?

ソフトウェアのテストは、大きく分けて「単体テスト」と「統合テスト」に分かれます。


単体テストは、「そのプログラムが単体で動作するかを確認する」ことを目的として行うテストです。

統合テストは、それらのプログラムを組み合わせて正常に動作するのかを確認します。


大規模なウォーターフォール型の開発においては、テストフェーズはさらに細かく分類されます。


・単体テスト

・結合テスト

・システムテスト

・運用テスト


として、徐々に統合する範囲を広めてテストを実施しています。

ウォーターフォール型開発のモデル概要図

単体テストは、長いテストフェーズのうち、いちばん初めに実施するテストであり、非常に重要な意味を持ちます。

単体テストの目的と役割

前提条件として、開発現場でのプログラミングにおいて、単一のプログラムだけで実行することはほとんどありません。

特に、オブジェクト指向をもとに設計されているのであれば、適宜ほかのプログラムを呼び出して利用することがほとんどです。


そんな中で、プログラムの実行時にエラーがあった場合、どのプログラムに問題があったのかを調査する必要があります。


エラーの発生したプログラムが他のプログラムを利用しているほど、原因調査に時間がかかってしまうのです。

そのため、統合テストではなるべくバグの発生しないことが望ましいとされています。


とはいえ、バグのまったくないソフトウェアを開発することは事実上不可能です。しかしながら、バグの発生を最小限に抑えることは可能です。


正しい単体テストを実施し、あらかじめバグを修正しておくことで、後工程でのバグ発生を最小限に抑えられます。

②Junitを使うメリット

JUnitは、ソフトウェアとして稼働するプログラムに対し、「プログラムが正しく動くのかを確認するテスト用のプログラム(テストコード)」を作ります。

作成したテストプログラムは、一度作ればいつでも再実行可能となるため、「何度も同じテストを実施することが可能」になります。


この「同じテストを繰り返し実施する」という操作は、大規模開発において非常に有用です。

ソフトウェア開発において、あとからバグや仕様変更によりプログラムの修正が行われることは日常茶飯事です。


また、プログラムの修正を行った場合には、そのプログラムだけでなく、他のプログラムにも修正の影響が出ていないかを確認する「回帰テスト(リグレッションテスト)」を実施します。


JUnitを使うことで単体テストが自動で実施可能であり、もし他のプログラムに影響を及ぼしている場合にはそのテストが失敗します。そのため、「ほかのプログラムに影響を及ぼしている」ことをすぐに検知できるのです。


もちろん、すべての影響範囲をJUnitで確認することはできません。しかし、回帰テストにかける工数を削減することが可能であるため、最終的に品質の高いソフトウェアができあがるのです。

2.JUnit5のインストール


まずは、EclipseにJUnit5をインストールする方法から紹介します。

Eclipseはインストールされているものとします。

①新しいプロジェクトを作成する

Eclipseを開き新しいプロジェクトを作成します。すでに作成済みのプロジェクトを利用する場合にはスキップしてください。


「ファイル > 新規 > Javaプロジェクト」を選択します。

ファイルタブからJavaプロジェクトを選択

任意のプロジェクト名を入力し、「完了」をクリックします。

Javaプロジェクトの作成画面

②JUnitをプロジェクトに読み込む

次に、プロジェクトでJUnitを利用できるようにします。


プロジェクトを右クリックし、「新規 > その他」を選択します。

ファイルタブからその他を選択

ウィザードが開かれるので、「JUnitテスト・ケース」を選択しましょう。

検索する場合には、以下のように「JUnit」と入力することで簡単に見つけられます。


「JUnitテスト・ケース」が見つかったら「次へ」をクリックします。

ウィザード画面でJUnitテストケースを選択

適当なクラス名を入力し、「完了」をクリックしましょう。

(今回はJUnitを有効化するための手順として紹介しています。正式な命名ルールについては後述します。)

テストケース作成画面

すると、以下のようなダイアログが表示されるので、そのまま「OK」を選択します。

「JUnit5ライブラリをビルドパスに追加するか」を問うダイアログ

以下のようにJUnitケースが生成され、エラーが表示されなければ準備完了です。

JUnitケースが生成された画面





3.JUnit5の基本的な使い方


それでは、JUnit5の基本的なルールから紹介していきます。

①基本ルール

JUnit5の基本的な使い方として、以下のルールに則ってテストケースを作成します。

テストクラスのルール

・テスト対象となるクラスに対し、対になるテストクラスを作成する

・作成するテストクラスのクラス名は「テスト対象クラス名Test」とする

・テスト対象クラスとテストクラスは同じパッケージとする

テストメソッドのルール

・@Testアノテーションを付与する

・戻り値はvoid(戻り値なし)とする

・引数は指定しない

②テスト対象のクラスを作成する

まずは、テストを実施する対象となる「プロダクションコード」を作成してみましょう。

今回は例として、「絶対値を計算して足し算する」というプログラムを作成してみます。

package jp.co.trainocamp.samples;

/**
 * 数値計算を行うクラス
 *
 */
public class Calculator {

  /**
   * 絶対値を利用した足し算
   * @param a 値1
   * @param b 値2
   */
  public static int absAdd(int a, int b) {
    
    if (a < 0) {
      a = a * -1;
    }
    if (b < 0) {
      b = b * -1;
    }
    
    return a + b;
  }
}

③テストコードを作成する

次に、作成した「Calculator」をテストするテストクラスを作成します。


テストクラスの作成ルールに則って、「CalculatorTest」という名前でクラスを作成します。

テストケースを作成する場合には、テスト対象となるクラスを選択してクラスを作成すると便利です。

テスト対象クラスを右クリックし、JUnitテスト・ケースを選択

インストール手順のときと同様に「JUnitテスト・ケース」を選択すると、以下のように表示され、クラス名やパッケージが自動で補完されています。


問題ないことを確認し、そのまま「完了」をクリックしましょう。

新規JUnitテストケースの作成画面

以下のように、CalculatorTestが自動で生成されます。

テストケースが自動生成された画面

次に「absAdd」メソッドのテストケースを作成しましょう。

テストクラスを以下のように書き換えます。

package jp.co.trainocamp.samples;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;

import org.junit.jupiter.api.Test;

public class CalculatorTest {

  @Test
  public void ともに自然数() {
    
    int result = Calculator.absAdd(3, 5);
    
    assertThat(result, is(8));
    
  }
}

④テストを実行する

テストクラスができあがったら、テストを実行します。

テストクラスを右クリックし、「実行 > JUnitテスト」を選択します。

テストを実行

すると、以下のように実行結果が表示されます。

テストの実行結果

成功の場合には緑で表示されます。



4.JUnit5の文法


一見普通のクラスと同じような構成をしていますが、JUnit5ではさまざまな「アノテーション」を駆使してテストケースを作成します。


ここでは、JUnit5を使いこなすうえで重要なアノテーションの使い方を解説していきます。

①@Testアノテーション

@Testアノテーションは、テストを実施する際に実行するメソッドへ付与します。


@TestアノテーションがないとJUnitはそのメソッドをテストケースとして認識しないため、「テストコードを記述したのにテストが実施されない」という場合にはアノテーションが正常に付与されているかを確認しましょう。

// このメソッドは実行される
@Test
void テストメソッド1() {
  
}

// @Testが記述されていないメソッドは実行されない
void テストメソッド2() {

}

②@BeforeEachアノテーション

データベースを扱う場合をはじめ、テスト対象となるクラスには共通の前処理が必要な場合もあります。


その場合に利用するのが「@BeforeEach」アノテーションです。

@BeforeEachアノテーションを付与したメソッドは、@Testを実行する直前に必ず実行されます。

@BeforeEach
void 前処理() {
  // 各テストの実施前に実行する処理を記述
}

@Test
void テスト1() {
}

@Test
void テスト2() {

}

上記のようなテストケースの場合、


前処理→テスト1 →前処理→テスト2


といった順番で実施されます。

テストの前後に実施するアノテーション

@BeforeEachと同様に、テストの前後に行う処理として以下のようなアノテーションが用意されています。


これらのアノテーションを利用することでより高度なテストを設定できますので、うまく活用しましょう。

アノテーション説明
@AfterEachテストケースが終了するたびに実行される
@BeforeAllすべてのテストケースを実行する前に一度だけこのメソッドの処理が実行される
@AfterAllすべてのテストケースが実行された後に一度だけこのメソッドの処理が実行される

③@DisplayNameアノテーション

先ほど紹介したテストメソッドは、名前が日本語になっていました。


しかし、本来はメソッドに日本語で説明をつけることは推奨されていません。

日本語や中国語、韓国語などは「ダブルバイト文字」と呼ばれるもので、英語圏のPCでは対応しておらず、エラーになる可能性があるからです。


だからといって、テスト用のメソッドを完全な英語にしてしまうと、どのテストケースだったのかわかりづらくなってしまいますよね。


そんなときに利用するのが@DisplayNameアノテーションです。

先ほどのテストケースを以下のように書き換えてみましょう。

package jp.co.trainocamp.samples;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class CalculatorTest {

  @Test
  @DisplayName("ともに自然数")
  public void naturals() {
    
    int result = Calculator.absAdd(3, 5);
    
    assertThat(result, is(8));
    
  }
}

このケースを再実施しても、以下のように日本語でケース情報が表示されます。

ケース情報が表示された画面

このように、@DisplayNameを利用することで安全にわかりやすいケース名を設定できます。

5.実際にJUnit5を使ってみよう

JUnitの基本的な使い方をマスターしたところで、Mavenを使ったJUnitを導入していきましょう。


多くの開発現場では、ビルドツールとしてMavenやGradleを利用して開発を進めています。JUnitの使い方と合わせて、これらのビルドツールの使い方もマスターすると良いでしょう。


今回は例として、EclipseとMavenを利用してJUnitを利用する方法を紹介します。

①前提条件

EclipseとMavenが導入済みであることとします。

②EclipseからMavenプロジェクトを作成する

まずは、Eclipseを利用してMavenプロジェクトを作成します。


「新規 > Mavenプロジェクト」を選択しましょう。

ファイルタブからMavenを選択

生成されるプロジェクトのパスを設定します。


今回は、シンプルなMavenプロジェクトを作成するため「シンプルなプロジェクトの作成」を選択したうえで、ロケーションに任意のパスを指定します。


入力が終わったら、「次へ」をクリックしましょう。

新規Mavenプロジェクトのロケーション選択画面

次に、Mavenの構成に必要な情報を入力します。


「グループId」と「アーティファクトId」の入力が必要ですが、任意の内容でかまいません。

今回は、例として以下の内容を入力します。


入力したら、「完了」をクリックします。

新規Mavenプロジェクトの構成入力画面

以下のようにプロジェクトが生成されれば完了です。

Mavenプロジェクトが生成された画面

③pom.xmlにJUnitの依存関係を追加する

プロジェクトが作成されたら、pom.xmlを開きます。


Mavenではpom.xmlに依存関係を追加することで、JUnitをはじめとするさまざまなライブラリをロードし利用できます。


pom.xmlに、以下のコードを追加しましょう。

<properties>
        <java.version>11</java.version>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <maven.compiler.source>${java.version}</maven.compiler.source>
    </properties>
<build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M5</version>
    </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-library</artifactId>
        <version>2.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

最終的には、以下のような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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jp.co.trainocamp.samples</groupId>
  <artifactId>maven-junit</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>JUnit Sample</name>
  <description>JUnitのサンプルプロジェクト</description>

  <properties>
    <java.version>11</java.version>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <maven.compiler.source>${java.version}</maven.compiler.source>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M5</version>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.7.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>2.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

pom.xmlを保存したら、プロジェクトを右クリックし、「実行 > Maven install」を選択します。

Mavenをinstall

以下のようにコンソールが起動し、「BUILD SUCCESS」と表示されればJUnitのインストールは完了です。

BUILD SUCCESSが表示されたコンソール

④プロダクションコードとテストコードを追加する

Mavenでは、プロダクションコードとテストコードを格納する場所が決まっています。


プロダクションコードは「src/main/java」へ、テストコードは「src/test/java」へ格納されます。

プロダクションコードとテストコードの格納場所

それでは、先ほどのCalculatorとCalculatorTestを作成してみましょう。


以下のように、Calculator.javaとCalculatorTest.javaが別の場所に生成されていれば、準備完了です。

それぞれ別に生成されている画面

では、テストを実行してみましょう。


Eclipse上からMavenでテストを実施する場合には、プロジェクトを右クリックし、「実行 > Maven test」を選択します。

Mavenでテストの実施を行う画面

以下のように、「CalculatorTest」が実行されたことが表示されれば実行完了です。

テストの実行結果が表示された画面




6.【応用】Junit5でカバレッジを取得してみよう


最後に、JUnitを使って「カバレッジ」を取得してみましょう。

カバレッジとはテストの「網羅性」を意味し、if文などの条件をすべて通っているかを確認するものです。


先ほど作成したCalculatorTestを用いてカバレッジを取得してみましょう。

①JaCoCoプラグインの導入

カバレッジの取得には、「JaCoCo」というプラグインを利用します。

以下のコードをpom.xmlへ追加しましょう。

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

最終的な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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jp.co.trainocamp.samples</groupId>
  <artifactId>maven-junit</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>JUnit Sample</name>
  <description>JUnitのサンプルプロジェクト</description>

  <properties>
    <java.version>11</java.version>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <maven.compiler.source>${java.version}</maven.compiler.source>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M5</version>
      </plugin>
      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.7</version>
        <executions>
          <execution>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
          <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
              <goal>report</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.7.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>2.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

pom.xmlを編集したら、再度「Maven install」を実行します。


すると、以下のようにJaCoCoのインストールとテストの再実行が行われます。

インストールと再実行が行われたコンソール画面

②カバレッジの確認

JaCoCoが出力したカバレッジは、targetフォルダに出力されます。

targetフォルダにカバレッジが格納されている画面

このフォルダを開き、Calculator.java.htmlを開いてみましょう。


すると、以下のように表示されています。

Calculator.javaのテスト結果、緑になっていない箇所がある画面

カバレッジの見方

カバレッジは、


・緑は実行された行

・赤は実行されていない行

・黄色は、実行されているがパターンが網羅されていない行

で表示されます。

つまり、正しくテストができている(カバレッジが100%)状態とは、「すべての行が緑になっている」ということです。


現時点では、aもbも自然数(正の値)でのみテストを実施しました。パターンを網羅するためには、以下のケースを実施する必要があります。

パターンab
(1)正の値正の値
(2)正の値負の値
(3)負の値正の値
(4)負の値負の値

今度は、テストケースを以下のように変更してみましょう。

package jp.co.trainocamp.samples;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class CalculatorTest {

	@Test
	@DisplayName("ともに自然数")
	public void naturals() {
		
		int result = Calculator.absAdd(3, 5);
		
		assertThat(result, is(8));
		
	}
	
	@Test
	@DisplayName("aが負の値")
	public void aMinus() {
		
		int result = Calculator.absAdd(-3, 5);
		assertThat(result, is(8));
	}
	
	@Test
	@DisplayName("bが負の値")
	public void bMinus() {
		int result = Calculator.absAdd(3, -5);
		assertThat(result, is(8));
	}
}

これを実行したうえで、再びJaCoCoが生成したHTMLのレポートを確認してみましょう。

メソッドのすべての行が緑になっていることを確認できました。

Calculator.javaのテスト結果、メソッドすべての行が緑になっている画面

カバレッジにこだわりすぎないように注意しよう

さて、ここで気になるのがパターン(4)に相当する、「aとbの両方が負の値の場合のテストをしていない」という点です。


ソースコード上は、引数のaとbについて、個別にマイナスかどうかの判定を行っています。

そのため、aかbのどちらかがマイナスであるパターンを網羅するだけで、カバレッジは100%になるのです。


このように、考えられる条件をすべて網羅しなくてもカバレッジが100%となる場合があります。

今回のケースでは問題はないですが、条件式が複雑なメソッドの場合、このテスト漏れが重大な問題を引き起こすこともあります。


「カバレッジが100%になったから完璧だ」という考えを持たず、「正しいテストは何か」をきちんと考えながらテストケースを作成するよう心がけましょう。



この記事をシェア