こんにちは、かつコーチです。
コードを書くたびにmainメソッドから動作確認して、確認が終わったらコードを消す……という作業を繰り返していませんか。
今回はJavaの標準的なテストフレームワークであるJUnit5(Javaのコードが正しく動くかを自動で検証する仕組み)の基本を解説します。
JUnit5とは?
なぜテストコードが必要なのか
mainメソッドにSystem.out.printlnを書いて目視で確認するやり方は、確認できたら消してしまうため同じ確認を何度もやり直すことになります。
機能を追加するたびに「前に作った機能が壊れていないか」を手作業で確認するのは非効率で、確認漏れも起きやすくなります。
テストコード(プログラムの動作を自動で検証するコード)を書いておけば、いつでも同じ確認を一瞬で繰り返せます。
JUnit5はJavaで最も広く使われているテストフレームワークで、Java 21環境でも標準的に使われています。
JUnit5の3つのモジュール
JUnit5は「JUnit Jupiter」「JUnit Platform」「JUnit Vintage」という3つのモジュールで構成されています。
実際にテストコードを書くときに使うのは主にJUnit Jupiter(JUnit5のテスト記述API)で、この記事でもJupiterのAPIを中心に解説します。
基本の書き方・実装手順
手順1:依存関係を追加する
Mavenの場合、pom.xmlに次の依存関係を追加します。
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
Gradleの場合はbuild.gradleに次を追加します。
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
test {
useJUnitPlatform()
}
GradleではuseJUnitPlatform()を忘れるとテストが実行されないので注意してください。
手順2:テストクラスを作る
テスト対象のクラスを用意します。
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
対応するテストクラスは、慣習としてクラス名 + Testという名前にし、src/test/java配下の同じパッケージに置きます。
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void add_2つの整数を足し算できる() {
Calculator calculator = new Calculator();
int result = calculator.add(2, 3);
assertEquals(5, result);
}
}
@Testアノテーションを付けたメソッドがテストとして実行されます。
JUnit5ではメソッド名に日本語を使うこともでき、テストの意図が読みやすくなるためこの書き方を採用しています。
手順3:ライフサイクルアノテーションで前後処理を書く
複数のテストで共通の準備・後片付けが必要な場合は、ライフサイクルアノテーションを使います。
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@AfterEach
void tearDown() {
calculator = null;
}
@Test
void add_2つの整数を足し算できる() {
assertEquals(5, calculator.add(2, 3));
}
}
@BeforeEachは各テストメソッドの実行前、@AfterEachは実行後に必ず呼ばれます。
テストごとに新しいインスタンスを用意したいときによく使う組み合わせです。
つまずきやすい設定・注意点
テストメソッドはpublicにする必要はなく、JUnit5ではパッケージプライベート(修飾子なし)で十分動作します。
逆にprivateにするとJUnitがテストとして認識できずエラーになるので、アクセス修飾子は「なし」かpublicにしましょう。
よくあるつまずきポイント・エラー対処
テストが1つも実行されずスキップされた
実際に私が業務で最初にハマったのは、GradleプロジェクトでuseJUnitPlatform()の設定を忘れていたケースです。
テストを実行しても「0 tests completed」とだけ表示され、原因の見当がつかず1時間ほど悩みました。
// ❌Before:useJUnitPlatformの指定がない
test {
}
この状態だとGradleは古いJUnit4形式でテストを探そうとするため、JUnit5の@Testアノテーションが付いたメソッドを見つけられません。
// ✅After:JUnit Platformを使うことを明示する
test {
useJUnitPlatform()
}
一行足すだけですが、これがないと本当に何も実行されないので、Gradleでの導入時は真っ先に確認すべき設定です。
応用・一歩先の使い方
テストの表示名をカスタマイズする
@DisplayNameアノテーションを使うと、テスト結果に表示される名前を自由に設定できます。
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Calculatorクラスのテスト")
class CalculatorTest {
@Test
@DisplayName("2つの正の整数を足すと正しい合計が返る")
void add_positiveNumbers() {
assertEquals(5, new Calculator().add(2, 3));
}
}
テストレポートに日本語の説明が表示されるため、チームでテスト結果を共有するときに意図が伝わりやすくなります。
テストの一部を無効化する
一時的にテストを実行したくない場合は@Disabledを使います。
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled("外部APIの仕様変更待ちのため一時無効化")
@Test
void callExternalApi() {
// ...
}
@Disabledには理由を書いておくと、あとで見返したときや他のメンバーが見たときに状況が分かりやすくなります。
まとめ
この記事のポイント
- JUnit5はJavaの標準的なテストフレームワークで、
@Testを付けたメソッドが自動テストとして実行される - Gradleでは
useJUnitPlatform()の設定を忘れるとテストが実行されない @BeforeEach・@AfterEachで各テストの前後処理を共通化できる@DisplayNameでテストに分かりやすい名前を付けられる
次に読むべき記事
- アサーションの書き方
- Mockitoでモックを使う
タグ: Java, 初心者向け, テスト