初めてのJUnit（基礎的な使い方）

今までCOBOLが業務の主流でしたが、最近Javaも使うようになったので、ここらで一度Java単体テストの手法として、JUnitの初歩的な使用方法を学習しようと思います。

## JUnitとは

JUnitについては、e-Wordsより以下のように記載されています。

>JUnitとは、Java言語で開発されたプログラムの単体テスト（ユニットテスト）を行なうためのソフトウェア。また、そこで用いられるテストコードの記述体系を含むテストフレームワーク。
_[IT用語辞典 e-Wordsより](https://e-words.jp/w/JUnit.html)_

JUnitを使うことで、作成したJavaクラスやメソッドの単体テストコードが記載できるようになります。JUnitには、単体テストをするにあたって活用できるメソッドやアノテーションが提供されています。

この記事では、以下の環境で実施します。

|環境|バージョン|
|:---:|:---:|
|端末|MacBook Air 2022 (M2)|
|OS|macOS Ventura 13.0.1|
|Java|JDK18|
|JUnit|5.6.0|

## テストクラス

基本的なテストクラスは、以下のとおりです。

```java
public class BasicTest {
    
    public BasicTest() {
    }
    
    @BeforeAll
    public static void setUpClass() {
    }
    
    @AfterAll
    public static void tearDownClass() {
    }
    
    @BeforeEach
    public void setUp() {
    }
    
    @AfterEach
    public void tearDown() {
    }

    @Test
    @DisplayName("Comment")
    public void testSomeMethod() {
    }
    
}
```

それぞれのメソッド（アノテーション）には、役割があります。

|アノテーション|解説|
|:---:|:---|
|`@BeforeAll`|テストクラスの最初に、必ず実行するセットアップメソッドです|
|`@AfterAll`|テストクラスの最後に、必ず実行するティアダウンメソッドです|
|`@BeforeEach`|各テストメソッドの前に、必ず実行するセットアップメソッドです|
|`@AfterEach`|各テストメソッドの後に、必ず実行するティアダウンメソッドです|
|`@Test`|実際のテストロジックを書くメソッドです|
|`@DisplayName`|JUnitテストクラス・メソッドの表示名です|

### `@BeforeAll`とコンストラクタの違い

コンストラクタは、インスタンス化時に一度だけ実行される初期化メソッドです。`@BeforeAll`と性質がよく似ているのですが、

* `@BeforeAll`は全てのテストを実行する前に実行
* コンストラクタは`@BeforeEach`の前に実行

されます。[^1]

[^1]: 参考：[https://b1san-blog.com/post/java/java-junit/#ライフサイクル](https://b1san-blog.com/post/java/java-junit/#%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB)

例えば、以下のようなテストコードを用意します。

```java
public class BasicTest {
    
    public BasicTest() {
        System.out.println("Constructor");
    }
    
    @BeforeAll
    public static void setUpClass() {
        System.out.println("@BeforeAll");
    }
    
    @AfterAll
    public static void tearDownClass() {
        System.out.println("@AfterAll");
    }
    
    @BeforeEach
    public void setUp() {
        System.out.println("@BeforeEach");
    }
    
    @AfterEach
    public void tearDown() {
        System.out.println("@AfterEach");
    }

    @Test
    @DisplayName("テスト１")
    public void testSomeMethod1() {
        System.out.println("@Test(1)");
    }

    @Test
    @DisplayName("テスト２")
    public void testSomeMethod2() {
        System.out.println("@Test(2)");
    }
    
}
```

こちらを実行してみると

```
@BeforeAll
Constructor　←@BeforeEachの前に実行
@BeforeEach
@Test(1)
@AfterEach
Constructor　←@BeforeEachの前に実行
@BeforeEach
@Test(2)
@AfterEach
@AfterAll
```

となります。

もし、コンストラクタを最初の一度だけ動かすようにしたい場合は、

```java:@TestInstance付加
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BasicTest {
	...
}
```

とすれば、最初の一度だけ動くようになります。

## アサーション

アサーションの意味については、e-Wordsにて、以下のように記載されています。

>アサーションとは、表明、断言、主張などの意味を持つ英単語。プログラミングにおいて、あるコードが実行される時に満たされるべき条件を記述して実行時にチェックする仕組みをアサーションという。
_[IT用語辞典 e-Wordsより](https://e-words.jp/w/%E3%82%A2%E3%82%B5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3.html)_

JUnitでは、このチェックする仕組みが提供されており、[Assertionsクラス](https://junit.org/junit5/docs/5.0.1/api/org/junit/jupiter/api/Assertions.html)のメソッドを用いて、値の検証を行います。

全ては書ききれませんので、上記サイトからいくつかピックアップしておきます。

### `assertEquals(A, B)`：AとBの同値判定

パラメータA、Bが同値であることを検証します。

```java:assertEqueals(A, B)
@Test
@DisplayName("同値判定")
public void testSomeMethod1() {
    int varA = 1;
    int varB = 1;
    assertEquals(varA, varB);
}
```

例ではint型同士ですが、オーバーロードによって、様々な型やオブジェクトの比較が可能になっています。

### `assertTrue(A)`：Aがtrueであるか判定

パラメータAがtrueであることを検証します。大小比較用のアサートは提供されていないので、こちらを使用します。

```java:assertTrue(A)
@Test
@DisplayName("コンディション判定")
public void testSomeMethod1() {
    int varA = 1;
    int varB = 2;
    assertTrue(varA < varB);
}
```

### `assertThrows(A, B)`：例外スロー判定

処理Bが例外Aを投げることを検証します。処理Bは、対象のメソッドを呼び出すか、ラムダ式でそのまま処理を記載します。

```java:assertThrows(A, B)
@Test
@DisplayName("例外スロー判定")
public void testSomeMethod1() {
    assertThrows(Exception.class, () -> {
        throw new Exception("Exception is Throwed.");
    });
}
```

## 簡単な単体テストを実践してみる

ここで、簡単な単体テストを実施してみようと思います。

### テストするコード

テストするコードは、パラメータの値に対して、`Fizz`/`Buzz`/`FizzBuzz`を返すメソッドです。

```java:テストするコード
public class FizzBuzz {
    
    public String FizzBuzz (int number) {
        String str = "";
        if (number % 3 == 0) {
            str += "Fizz";
        }
        if (number % 5 == 0) {
            str += "Buzz";
        }
        return str;
    }
    
}
```

### テストケース

今回は、以下のケースを検証します。

|ケースNo.|実施条件（パラメータ）|想定結果（返却値）|
|:---:|:---|:---|
|ケース１|3で割り切れ、5で割り切れない値|`Fizz`|
|ケース２|3で割り切れず、5で割り切れる値|`Buzz`|
|ケース３|3でも5でも割り切れる値|`FizzBuzz`|
|ケース４|3でも5でも割り切れない値|（空文字）|

### テストコード

テストケースに基づいて作成したJUnitソースは以下のとおりです。

```java:JUnitコード
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class FizzBuzzTest {
    
    private FizzBuzz fz;
    
    /**
     * FizzBuzzクラスはコンストラクタで一度だけインスタンス化
     * ※ @TestInstance(TestInstance.Lifecycle.PER_CLASS)のため、コンストラクタは一度しか実行しない
     */
    public FizzBuzzTest() {
        fz = new FizzBuzz();
    }

    /**
     * 実施条件：3で割り切れ、5で割り切れない値
     * 想定結果：Fizz
     */
    @Test
    @DisplayName("ケース１")
    public void testFizzBuzz1() {
        assertEquals("Fizz", fz.FizzBuzz(9));
    }
    
    /**
     * 実施条件：3で割り切れず、5で割り切れる値
     * 想定結果：Buzz
     */
    @Test
    @DisplayName("ケース２")
    public void testFizzBuzz2() {
        assertEquals("Buzz", fz.FizzBuzz(10));
    }
    
    /**
     * 実施条件：3でも5でも割り切れる値
     * 想定結果：FizzBuzz
     */
    @Test
    @DisplayName("ケース３")
    public void testFizzBuzz3() {
        assertEquals("FizzBuzz", fz.FizzBuzz(15));
    }
    
    /**
     * 実施条件：3でも5でも割り切れない値
     * 想定結果：（空文字）
     */
    @Test
    @DisplayName("ケース４")
    public void testFizzBuzz4() {
        assertEquals("", fz.FizzBuzz(8));
    }
    
}
```

### 検証結果

NetBeansなどのIDEでは、検証結果をグラフィカルに確認することができます。

![Fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163288/6c12f20d-c90a-89d1-50e4-b70c380b2fa4.png)

## まとめ

初めてJUnitの基礎的な使い方を調べて、お気持ち程度の単体テストまで実施しました。調べていると、もっと色々な使い方がありましたので、もっと調べて活用できればと思います。