きり丸の技術日記

技術・エンジニアのイベント・資格等はこちらにまとめる予定です

もっといいテストが書きたい(AssertEqualsとAssertThatの検証)

AssertEqualsとAssertThatの挙動の違いがよくわかってないところがあったので検証しました。

検証したくなった理由

個人的にはAssertEqualsで全部行えばいいと思ってました。
assertThatでしかできないことというのは、Equalsよりも弱い検証になりがちなので、必要だとは思えなかったからです。
ただ、AssertEqualsに不満がある点もあるので、自分の中で折り合いをつけるために今回の検証を行っています。

なお、結論自体は出ていません。
今後のテストでAssertThatもちゃんと使って行こうかな、って認識を改めた程度です。

今回の検証ソース

AssertX.java
自分への検証ソースのため、今後大きく変更する可能性もあります。

今回の記事に出てくるライブラリ

今回の検証していたライブラリを挙げておきます。

メソッド ライブラリ
AssertAll org.junit.jupiter.api.Assertions.assertAll
AssertEquals org.junit.jupiter.api.Assertions.assertEquals
SoftAssertions org.assertj.core.api.SoftAssertions
AssertThat org.assertj.core.api.Assertions.assertThat

検証

AssertEquals

assertEquals(expected, actual, {message})

一致条件のみ検証できるメソッド。
パラメータがactual, expectedという順番であれば、最高だったが、そうではないのでイケてない。
第三パラメータのmessageは無くても問題ないが、あるとエラー時に何を検証したかったのかが表示できるようになる。

このパラメータが活きてくるのは、AssertAllと併用した時である。
assertは失敗すると、失敗した行以降の検証が行われないが、assertAllを使用した時、全てテストしてくれるようになる。
ただし、AssertAllにも問題があり、何かのテストが失敗したことは分かるものの、具体的に何が失敗したのかが分からないようになってしまう。

そこで、assertEqualsの第三パラメータを使用することで、何を検証しようとして失敗したのかをトレースすることができる。
個人的には、具体的な値を入れることよりも、トレースのための材料とすることがメインの目的のため、適当に1,2,3等でも問題ないと思っている。

assertAll(
    () -> assertEquals(1, list.size(), "size"), 
    () -> assertEquals(null, list.get(0), "乱太郎"),
    () -> assertEquals(null, list.get(1), "きり丸"),
    () -> assertEquals(null, list.get(2), "新兵衛")
);

エラーメッセージ一部抜粋

org.opentest4j.MultipleFailuresError: Multiple Failures (4 failures)
    size ==> expected: <1> but was: <3>
    乱太郎 ==> expected: <null> but was: <AssertX.Ninja(name=乱太郎, age=10, sex=1)>
    きり丸 ==> expected: <null> but was: <AssertX.Ninja(name=きり丸, age=9, sex=1)>
    新兵衛 ==> expected: <null> but was: <AssertX.Ninja(name=新兵衛, age=8, sex=2)>

AssertThat

    assertThat(actual){.as(message)}.検証したいメソッド(expected).検証したいメソッド2(expected)...

色んなことが検証できるメソッド。その点は素晴らしい。
hasSizeやisEqualTo, hasFieldOrPropertyWithValue等々のメソッド名を見れば何を検証したいのかがわかる。

個人的に嫌いな点として、actualとexpectedの間にmessageを定義しなければならないので、ノイズになってしまう。
ただ、メソッド見れば検証したいことがわかるので、そもそもmessageはノイズ。
この感覚はたくさんテスト書けば自分の中でのケリがつくかと思っている。 あとは、メソッド見ればわかる、というレベルに落とし込むのであれば、詳細なメソッドを知っておく必要があるので、昔の自分にとってはすこしハードルが高かった。

また、assertAllとassertThatは併用しない方がいい。
そもそも、ライブラリが違うので、自分が期待していない挙動を行った。
(具体的には下に記載)

assertAllと同じ挙動を期待するのであれば、SoftAssertionsを使用したほうがいい。
softAssertionsのassertAllで一度に検証することができる。

SoftAssertions softly = new SoftAssertions();
softly.assertThat(list).hasSize(1);
softly.assertThat(list.get(0)).as("乱太郎").isEqualTo(null);
softly.assertThat(list.get(1)).as("きり丸").isEqualTo(null);
softly.assertThat(list.get(2)).as("新兵衛").isEqualTo(null);

softly.assertAll();

エラーメッセージ一部抜粋

org.opentest4j.MultipleFailuresError: Multiple Failures (4 failures)
    
Expected size:<1> but was:<3> in:
<[AssertX.Ninja(name=乱太郎, age=10, sex=1),
    AssertX.Ninja(name=きり丸, age=9, sex=1),
    AssertX.Ninja(name=新兵衛, age=8, sex=2)]>
at AssertX._assertThat(AssertX.java:60)
    [乱太郎] 
Expecting:
 <AssertX.Ninja(name=乱太郎, age=10, sex=1)>
to be equal to:
 <null>
but was not.
at AssertX._assertThat(AssertX.java:62)
    [きり丸] 
Expecting:
 <AssertX.Ninja(name=きり丸, age=9, sex=1)>
to be equal to:
 <null>
but was not.
at AssertX._assertThat(AssertX.java:67)
    [新兵衛] 
Expecting:
 <AssertX.Ninja(name=新兵衛, age=8, sex=2)>
to be equal to:
 <null>
but was not.
at AssertX._assertThat(AssertX.java:68)

AssertAll と assertThatを併用した場合

assertAll と assertThat を併用しないほうがいいといったが、上のように1行に1つしか検証しない場合は特に問題が起きない。
問題は以下のように、一つの項目に対して複数の検証を行おうとしたときである。
期待していたのは、name, age, sexの検証という1行1行の検証だったが、実際の挙動は全部を3回検証するという結果になってしまった。

ただ無駄に試行回数を増やしているだけになるので、使わないほうがいいと思う。
(先に述べたように、そもそもライブラリが違うから相性も良くないでしょうし)

assertAll()
  () ->  assertThat(list.get(0)).as("乱太郎")
          .hasFieldOrPropertyWithValue("name", null)
          .hasFieldOrPropertyWithValue("age", null)
          .hasFieldOrPropertyWithValue("sex", null)
);

エラーメッセージ一部抜粋 以下のメッセージが3回出現する。

Expecting
  <AssertX.Ninja(name=乱太郎, age=10, sex=1)>
to have a property or a field named <"name"> with value
  <null>
but value was:
  <"乱太郎">

類似記事

nainaistar.hatenablog.com


カテゴリー「テスト」で他にもテストで使える技術を書いてるので、そちらもよろしくお願いします。

nainaistar.hatenablog.com