0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JUnit学習①

Last updated at Posted at 2025-04-13

ポケモンバトルを題材にした共通フィクスチャとテストのカテゴリ化をJUnitで学ぶ①

職場でユニットテストをを書く機会があったのですが、そのそもユニットテストを書いたことない自分からすると最初から何を理解すればいいかわからなかったため、自分用の記事にしました。


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

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

前提条件

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

テスト対象:PokemonPokemonBattle クラス

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

Pokemon クラス

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 くらs

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 を使用して、各テストメソッドの前に共通のポケモンインスタンスとバトルインスタンスを初期化してます。

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 を使用することで、各テストメソッドでの初期化コードを共通化し、コードの重複を避けることがでます。

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

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

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

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

@BeforeEachがない例

@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」などのカテゴリに分類します

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 を使用することで、テストを分類し、特定のカテゴリのテストのみを実行することがきます

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

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

最後に

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

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

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

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?