JUnit学習①


---

#  ポケモンバトルを題材にした共通フィクスチャとテストのカテゴリ化をJUnitで学ぶ①
:::note warn
職場でユニットテストをを書く機会があったのですが、そのそもユニットテストを書いたことない自分からすると最初から何を理解すればいいかわからなかったため、自分用の記事にしました。
:::

---

:::note warn
この記事はプログラミング初心者が自分のメモ用として書いている記事です。
そのため、誤った情報が記述されている可能性があります。
自己判断で読み進めてください
:::

JUnit 5では、テストの効率化と可読性向上のために、共通の初期化処理を行う「共通フィクスチャ」と、テストを分類する「カテゴリ化」の機能が提供されていますこの記事では、ポケモンバトルを題材に、これらの機能の使い方を具体的なコード例とともに解説します

##  前提条件

 JUnit 5（JUnit Jupiter）を用
 Javaでの開発環境が整ってる
 ポケモンバトルの基本的な概念に馴染みがる
 使用するポケモンはピカチュウとイーブイ（可愛いよね）
---

##  テスト対象：`Pokemon` と `PokemonBattle` クラス

まず、テスト対象となる `Pokemon` クラスと `PokemonBattle` クラスを定義しす

### `Pokemon` クラス



```java
public class Pokemon {
    private String name;
    private int hp;
    private int attackPower;

    public Pokemon(String name, int hp, int attackPower) {
        this.name = name;
        this.hp = hp;
        this.attackPower = attackPower;
    }

    public int getHp() {
        return hp;
    }

    public int getAttackPower() {
        return attackPower;
    }

    public void receiveDamage(int damage) {
        this.hp = Math.max(this.hp - damage, 0);
    }

    public String getName() {
        return name;
    }
}
```



### `PokemonBattle` くらｓ

```java
public class PokemonBattle {
    private Pokemon attacker;
    private Pokemon defender;

    public PokemonBattle(Pokemon attacker, Pokemon defender) {
        this.attacker = attacker;
        this.defender = defender;
    }

    public void attack() {
        int damage = attacker.getAttackPower();
        defender.receiveDamage(damage);
    }

    public Pokemon getAttacker() {
        return attacker;
    }

    public Pokemon getDefender() {
        return defender;
    }
}
```



---

##  共通フィクスチャ：`@BeforeEach` の用意

共通フィクスチャとは、各テストメソッドの実行前に共通の初期化処理を行う仕組す。JUnit 5では、`@BeforeEach` アノテーションを使用して実現ます。

以下のテストクラスでは、`@BeforeEach` を使用して、各テストメソッドの前に共通のポケモンインスタンスとバトルインスタンスを初期化してます。



```java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class PokemonBattleTest {
    private Pokemon pikachu;
    private Pokemon eievui;
    private PokemonBattle battle;

    @BeforeEach
    public void setUp() {
        pikachu = new Pokemon("Pikachu", 100, 20);
        eievui = new Pokemon("Eievui", 100, 15);
        battle = new PokemonBattle(pikachu, eievui);
    }

    @Test
    public void testAttackReducesHp() {
        battle.attack();
        assertEquals(80, eievui.getHp());
    }
}
```



このように、`@BeforeEach` を使用することで、各テストメソッドでの初期化コードを共通化し、コードの重複を避けることがでます。

:::note warn
@BeforeEachがないとどうなる？
共通フィクスチャがないと、テストごとに手動で初期化コードを書く必要があり、以下のようなデメリットが発生します
:::

 コード重複：すべてのテストで同じ new Pokemon(...) を繰り返すことに。

 可読性低下：どのポケモンを使っているか毎回読み解く必要がある。

 メンテナンス性低下：ポケモンの初期HPを変更したいとき、全テストに修正が必要になる


@BeforeEachがない例
```Java
@Test
public void testPikachuVsEievui() {
    Pokemon pikachu = new Pokemon("Pikachu", 100, 20);
    Pokemon eievui = new Pokemon("Eievui", 100, 15);
    PokemonBattle battle = new PokemonBattle(pikachu, eievui);
    battle.attack();
    assertEquals(80, eievui.getHp());
}

```
---

##  テストのカテゴリ化：`@Tag` 活用

テストのカテゴリ化とは、テストメソッドやクラスにタグを付与し、特定のタグを持つテストのみを実行するなどのフィルタリングを可能にする仕様です。JUnit 5では、`@Tag` アノテーションを使用して実行します

以下のテストクラスでは、`@Tag` を使用して、テストメソッドを「critical」や「edge」などのカテゴリに分類します



```java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class PokemonBattleTest {
    private Pokemon pikachu;
    private Pokemon eievui;
    private PokemonBattle battle;

    @BeforeEach
    public void setUp() {
        pikachu = new Pokemon("Pikachu", 100, 20);
        eievui = new Pokemon("Eievui", 100, 15);
        battle = new PokemonBattle(pikachu, eievui);
    }

    @Test
    @Tag("critical")
    public void testCriticalHit() {
        int criticalDamage = pikachu.getAttackPower() * 2;
        eievui.receiveDamage(criticalDamage);
        assertEquals(60, eievui.getHp());
    }

    @Test
    @Tag("edge")
    public void testAttackWhenHpIsZero() {
        eievui.receiveDamage(100);
        assertEquals(0, eievui.getHp());
        battle.attack();
        assertEquals(0, eievui.getHp(), "HPが0のときに攻撃を受けてもHPは0のまま");
    }
}
```


このように、`@Tag` を使用することで、テストを分類し、特定のカテゴリのテストのみを実行することがきます

:::note warn
なぜカテゴリ化（@Tag）が必要？
テストが1〜2個しかないときは不要に感じますが、実際のテストでは以下のような要望が出てくる時があります。

 「開発中の機能のテストだけを実行したい」
  「エッジケースのテストだけを隔離してチェックしたい」　
  そんなときに便利なのが @Tag!
:::

## 最後に
ポケモンのテストを書くの楽しい。例題がイメージしやすく、JUnitの使い方がよく分かった。

テストの数が3個を超えたら、タグでの分類が便利になってくる。

@BeforeEach をサボるとテスト修正時に地獄をみる（絶対に共通セットアップにまとめる）