こんにちは、かつコーチです。
前回はJUnit5の基本を解説しましたが、テストの心臓部といえるのが「期待した結果と実際の結果を比較する」処理です。
今回はJUnit5でこの比較を行うアサーション(テストの期待値と実際の値を照合し、一致しなければテストを失敗させる仕組み)の書き方を、具体例とともに解説します。
アサーションとは?
なぜアサーションが必要なのか
テストメソッドを実行しただけでは、コードが例外を投げない限り「成功」と見なされてしまいます。
これではadd(2, 3)が本当に5を返しているかどうかは確認できません。
アサーションを使うことで「この値はこうあるべき」という期待を明示し、期待と異なる結果が出た瞬間にテストを失敗させられます。
JUnit5ではorg.junit.jupiter.api.Assertionsクラスに用意されたassertXxxという静的メソッド群を使います。
基本の書式
import static org.junit.jupiter.api.Assertions.assertEquals;
assertEquals(期待値, 実際の値);
多くのassertXxxメソッドは「期待値」を第1引数、「実際の値」を第2引数に取ります。
この順序を逆にしても動作はしますが、失敗時のメッセージが分かりにくくなるため、順序は必ず守りましょう。
基本の書き方・実装手順
assertEquals:値が等しいかを確認する
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class StringUtilTest {
@Test
void toUpperCase_小文字を大文字に変換できる() {
String result = "hello".toUpperCase();
assertEquals("HELLO", result);
}
}
最もよく使うアサーションで、数値・文字列・オブジェクトの比較全般に使えます。
オブジェクトの比較には内部でequals()が使われるため、比較対象のクラスでequals()が正しく実装されている必要があります。
assertTrue・assertFalse:条件の真偽を確認する
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
class NumberUtilTest {
@Test
void isEven_偶数を正しく判定できる() {
assertTrue(NumberUtil.isEven(4));
assertFalse(NumberUtil.isEven(5));
}
}
真偽値を返すメソッドの検証に使いますが、assertTrue(a == b)のように使うと失敗時に「なぜ違うのか」が分かりにくいメッセージになりがちです。
値の比較にはassertEquals、真偽の意味そのものを確認したい場合にはassertTrue/assertFalse、と使い分けるのがコツです。
assertThrows:例外の発生を確認する
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void divide_0で割ると例外が発生する() {
Calculator calculator = new Calculator();
ArithmeticException exception = assertThrows(
ArithmeticException.class,
() -> calculator.divide(10, 0)
);
assertEquals("/ by zero", exception.getMessage());
}
}
「例外が発生すること自体」も仕様の一部なので、正常系だけでなく異常系のテストにもassertThrowsは欠かせません。
第2引数にラムダ式で例外を起こす処理を渡し、戻り値として発生した例外インスタンスを受け取れるので、メッセージまで検証できます。
assertAll:複数の検証をまとめて実行する
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
class UserTest {
@Test
void createUser_名前とメールが正しく設定される() {
User user = new User("かつコーチ", "info@katsu-coach.com");
assertAll(
() -> assertEquals("かつコーチ", user.getName()),
() -> assertEquals("info@katsu-coach.com", user.getEmail())
);
}
}
assertEqualsを複数行並べただけだと、最初の1つが失敗した時点で残りは実行されず、まとめて何が壊れているのか分かりません。assertAllでまとめると、すべての検証を実行したうえで失敗した項目をまとめて報告してくれます。
よくあるつまずきポイント・エラー対処
assertEqualsの引数順を逆にしてメッセージが読みにくくなった
実際に私がレビューで指摘を受けたのが、期待値と実際の値の順番を逆に書いていたケースです。
// ❌Before:実際の値が第1引数になっている
int result = calculator.add(2, 3);
assertEquals(result, 5);
このコードでもテストは正しく動作しますが、テストが失敗したときのメッセージがexpected: <5> but was: <5>のように意味の伝わらない表示になり、原因調査に無駄な時間がかかりました。
// ✅After:期待値を第1引数に、実際の値を第2引数にする
int result = calculator.add(2, 3);
assertEquals(5, result);
「期待値が先、実際の値が後」というルールを徹底しておくだけで、失敗時のメッセージから何が違うのかを一目で判断できるようになります。
応用・一歩先の使い方
失敗時のメッセージをカスタマイズする
import static org.junit.jupiter.api.Assertions.assertEquals;
assertEquals(5, calculator.add(2, 3), "add(2, 3)の結果が期待値と異なります");
第3引数にメッセージを渡すと、失敗時にその文言が表示されます。
チームでテストを共有する場合、意図が伝わりやすくなるので積極的に活用しましょう。
なお、メッセージを毎回文字列として組み立てるとテストが遅くなるという指摘もあるため、複雑な文字列連結が必要な場合はSupplier<String>を渡す形(assertEquals(5, result, () -> "詳細メッセージ"))を使うとパフォーマンスへの影響を避けられます。
assertEqualsとassertSameの違い
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
String a = new String("hello");
String b = new String("hello");
assertEquals(a, b); // 成功:中身が同じなのでOK
assertSame(a, b); // 失敗:別インスタンスなのでNG
assertEqualsはequals()による内容の比較、assertSameは==による同一インスタンスかどうかの比較です。
この違いを理解していないと「なぜassertSameだけ失敗するのか」で混乱するので、意味の違いを押さえておきましょう。
まとめ
この記事のポイント
- アサーションは期待値と実際の値を比較し、一致しなければテストを失敗させる仕組み
assertEqualsは「期待値、実際の値」の順で書くのが鉄則- 異常系のテストには
assertThrows、複数検証をまとめるにはassertAllが便利 assertEqualsは内容比較、assertSameは同一インスタンス比較という違いがある
次に読むべき記事
- JUnit5の基本
- Mockitoでモックを使う
タグ: Java, 初心者向け, テスト