[JUnit5]assertを途中でエラーが起きても最後まで実行したい?そんな時はassertAllメソッドとmessage引数を使おう

2024/03/15に公開

JUnit5のassertAllメソッドは、複数のアサーションを一度に実行するための便利なメソッドです。途中のassertでエラーが起きても最後まで流れるようにすることができます。
複数のアサーションを一度に実行できるため、テストが失敗した箇所をまとめて修正しやすくなり開発効率が上がります。

しかし、assertAllをそのまま使用すると、どのアサーションが失敗したのか特定するのが難しくなることがあります。
そのため、各assertXXXメソッドを呼び出す際に、最後の引数の message を指定すると良いでしょう。

前置き

実行環境は概ね以下のとおりです

  • JDK 17
  • junit-bom 5.9.1
  • Windows 11
  • IntelliJ IDEA

コード例1

以下に、assertAllメソッドを使用したテストケースの例を示します。この例では、assertEqualsメソッドの最後の引数としてエラーメッセージを指定しています。

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class SampleTest {
    @Test
    void test() {
        String actual = "JUnit4";
        assertAll("値の比較",
                () -> assertEquals("JUnit5", actual, "文字列不一致"),
                () -> assertEquals(6, actual.length(), "長さ不一致")
        );
    }
}

このコードでは、assertAllメソッド内で複数のassertEqualsメソッドを呼び出しています。各assertEqualsメソッドの最後の引数には、アサーションが失敗した場合に表示されるメッセージを指定しています。
こう書くことで、テストが失敗した場合でも、どのアサーションが問題だったのかをすぐに特定することができます。

実行結果

org.opentest4j.MultipleFailuresError: 値の比較 (1 failure)
	org.opentest4j.AssertionFailedError: 文字列不一致 ==> expected: <JUnit5> but was: <JUnit4>
	at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:80)
	at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:44)
	at org.junit.jupiter.api.Assertions.assertAll(Assertions.java:2929)
	at SampleTest.test(SampleTest.java:10)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    (中略)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
	Suppressed: org.opentest4j.AssertionFailedError: 文字列不一致 ==> expected: <JUnit5> but was: <JUnit4>
		at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
		at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
		at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
		at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
		at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1153)
		at SampleTest.lambda$test$0(SampleTest.java:11)
		at org.junit.jupiter.api.AssertAll.lambda$assertAll$0(AssertAll.java:68)
		at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
		at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
		at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
		at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
		at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
		at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
		at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
		at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:77)
		... 87 more


文字列不一致
予想:JUnit5
実際:JUnit4
<クリックで差分を表示>

org.opentest4j.AssertionFailedError: 文字列不一致 ==> expected: <JUnit5> but was: <JUnit4>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
	at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1153)
	at SampleTest.lambda$test$0(SampleTest.java:11)
	at org.junit.jupiter.api.AssertAll.lambda$assertAll$0(AssertAll.java:68)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:77)
	at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:44)
	at org.junit.jupiter.api.Assertions.assertAll(Assertions.java:2929)
	at SampleTest.test(SampleTest.java:10)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	    (中略)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)


> Task :test FAILED
SampleTest > test() FAILED
    org.opentest4j.MultipleFailuresError at SampleTest.java:10
        Caused by: org.opentest4j.AssertionFailedError at SampleTest.java:11
1 test completed, 1 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///E:/work/java17-sample/build/reports/tests/test/index.html
* Try:
> Run with --scan to get full insights.

コード例2

今度は、複数のエラーを同時に発生させてみます。失敗するアサーションが複数であったとしても同時にアサーション結果を表示できるか確認しましょう。

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class SampleTest {
    @Test
    void test2() {
        String actual = "JUnit4";
        assertAll("値の比較",
                () -> assertEquals("JUnit5", actual, "文字列不一致"), // このアサーションは失敗する
                () -> assertEquals(6, actual.length(), "長さ不一致"), // このアサーションは成功する
                () -> assertEquals(7, actual.length(), "長さ不一致") // このアサーションは失敗する
        );
    }
}

実行結果

このように、エラーが起きるアサーションを列挙することができました。何のアサーションがエラーを起こしているのかが見やすくなりました。

org.opentest4j.MultipleFailuresError: 値の比較 (2 failures)
	org.opentest4j.AssertionFailedError: 文字列不一致 ==> expected: <JUnit5> but was: <JUnit4>
	org.opentest4j.AssertionFailedError: 長さ不一致 ==> expected: <7> but was: <6>

(中略)

文字列不一致
予想:JUnit5
実際:JUnit4

(中略)

長さ不一致
予想:7
実際:6

まとめ

JUnit5のassertAllメソッドと message 引数をうまく使いこなすことで、より効率的なユニットテストを書くことができます。ぜひ試してみてください。

FYI

https://junit.org/junit5/docs/5.9.1/api/

Discussion