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