きり丸の技術日記

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

【Java】よく使うassertThatのメソッド集【AssertJ】

「assertThat」と検索すると私のブログに引っかかる人がいるようなので、この記事を書きました。ただ「AssertJ」と検索すると、たくさん有用な記事も見つかります。

なので、この記事は「AssertJ徹底解説」ではなく、「きり丸がよく使うAssertJのメソッド」というところで新規性を出していきます。

環境

  • JUnit 5
  • AssertJ
    • org.assertj:assertj-core:3.13.2

※ 上記を指定せず、下記で一気にimportしています。 org.springframework.boot:spring-boot-starter-test:2.2.4.RELEASE を使用しています。

まとめ

  • 必須級
    • isEqualTo
    • isInstanceOfSatisfying
  • 準必須級
    • SoftAssertions
  • あったら便利
    • hasSize

だけ覚えれば問題ありません。

必須級

isEqualTo


比較対象が同一であることを確認します。基本中の基本です。ただし、isEqualToで使うために不必要に情報を丸めてしまい、エラーメッセージが適切でないことがあるので注意。ちゃんとエラーメッセージのレポートを確認しながら、テストしましょう。

String actual = "123";
String expect = "123";

assertThat(actual).isEqualTo(expect);

なお、Javaの仕様上、クラスでequalsメソッドが実装されていないと、インスタンスが違う場合は比較できません。めんどくさいので、わたしはLombok@Data@Valueで自動生成させてます。

  @Test
  void test_001(){
    Animals expect = new Animals("100");
    Animals actual = new Animals("100");

    assertThat(actual).isEqualTo(expect);
  }

NGなパターン。

  class Animals{
    String id;
    Animals(String id){
        this.id = id;
    }
  }

エラーメッセージ。

Expecting:
 <jp.co.kelly.biz.domain.NestedList$Animals@268d9b4b>
to be equal to:
 <jp.co.kelly.biz.domain.NestedList$Animals@3098daa7>

OKなパターン。

  class Animals{
    String id;
    Animals(String id){
        this.id = id;
    }

    @Override
    public boolean equals(Object obj){
      Animals animals = (Animals)obj;
      return this.id.equals(animals.id);
    }
  }

同じインスタンスであることを確認したい場合は、isSameAsがありますが…。私は中身が同じであれば、インスタンスが別でもいいので、isSameAsは使わないです。

isInstanceOfSatisfying

エラーのハンドリングが正しいことを確認する。assertThatThrownByと組み合わせる。

// Bookクラス
public class Book{
    String isbn;
    public Book(String isbn){
        if (isbn.length() == 13){
            throw new RuntimeException("ISBNの桁数が正しくない");
        }
    }
}

エラーが発生しうる条件でassertThatThrownByメソッドを呼びます。isInstanceOfSatisfyingには第一引数に目的のExceptionのクラス、第二引数にExceptionの中身を確認します。もし、Exceptionが発生することだけを確認したければ、isInstanceOfでも問題ありません。

assertThatThrownBy(
    () -> new Book("1")
).isInstanceOfSatisfying(
    RuntimeException.class,
    (e) -> assertThat(e.getMessage()).isEqualTo("ISBNの桁数が正しくない")
);

// Exceptionの中身は無視したい場合
assertThatThrownBy(
    () -> new Book("1")
).isInstanceOf(
    RuntimeException.class
);

同等のメソッドをassertThatThrownByとisInstanceOfSatisfyingを使わなかった時の書き方も記載しておきます。正常終了しない場合と目的以外のExceptionが発生した場合を確認しなければならないので、書き方が非常に大変です。

try{
    new Book("1");
    fail();
} catch (RuntimeException e){
    assertThat(e.getMessage()).isEqualTo("ISBNの桁数が正しくない")
} catch(Exception e){
    fail();
}

準必須級

SoftAssertions


複数のテストを一気にテストができるようになります。例えば、下記の例でhasSizeで100と指定すると通常であれば後続のテストが動きません。SoftAssertionsを使用すれば、途中で失敗しても全てテストしてくれます。ただし、これはテストを後回しにする方法なので、assertAllを呼び忘れるとテストされずに正常終了します。

List actual = List.of("1", "2");
List expect = List.of("1", "2");

SoftAssertions softly = new SoftAssertions();
softly.assertThat(actual).hasSize(2);
softly.assertThat(actual).isEqualTo(expect);
softly.assertThat(actual).contains("1", "2");

// ここで一気に比較する。忘れてはいけない!
softly.assertAll();

これはチェイン的な書き方しても、メリットがあります。通常通りであれば、下記のエラーメッセージしか出てきません。

assertThat(actual)
  .hasSize(100)
  .isEqualTo(null)
  .contains("1","2","3");

エラーメッセージ。

Expected size:<100> but was:<2> in:
<["1", "2"]>
at jp.co.kelly.biz.domain.AssertX.test_xxx(AssertX.java:95)

しかし、SoftAssertionsを組み合わせることで、一度のテストで下記のメッセージが出力されます。特にエラーの行数も一緒に出力されるので、かなり好きです。

softly.assertThat(actual)
  .hasSize(100)
  .isEqualTo(null)
  .contains("1","2","3");

softly.assertAll();
Expected size:<100> but was:<2> in:
<["1", "2"]>
at AssertX.test_xxx(AssertX.java:95)
    
Expecting:
 <["1", "2"]>
to be equal to:
 <null>
but was not.
at AssertX.test_xxx(AssertX.java:96)
    
Expecting:
 <["1", "2"]>
to contain:
 <["1", "2", "3"]>
but could not find:
 <["3"]>

at AssertX.test_xxx(AssertX.java:97)

なお、私はチェイン的な書き方よりも、1行1行確認する方が好きです。

書き方としては優れていますが、テスト成功している行に対して処理を追加する必要があります。そのことにより、既存のテストを壊してしまう可能性があることに違和感があります。

この辺に関しては好みですが、テストはできる限りシンプルにしたいので1行1行確認する方が好きです。


他の言語でも同様の挙動するメソッドがあるはずなので調べてみてください。Pythonではsubtestというメソッドが同等の動きをする認識です。

あったら便利

hasSize


Listのサイズを確認できます。最終的にはhasSizeの確認は不要になりますが、TDDで小さく確認するときの足掛かりになるので、私はまずサイズ確認してからそのあとリストのisEqualToを使用しています。

List actual = List.of(1, 2, 3);
List actual = List.of(1, 2, 3);

assertThat(actual).hasSize(3);
assertThat(actual).isEqualTo(expect);

Collection型のsize()を使ってisEqualToを使う方法もありますが、中身の検証をしたいのか、サイズの検証をしたいのか一見で分からなくなります。なので、sizeに関してはhasSizeを使用しています。

assertThat(actual.size()).isEqualTo(3); // サイズを知りたい?
assertThat(actual.get(2)).isEqualTo(3); // 中身を知りたい?

まとめ

  • 必須級
    • isEqualTo
    • isInstanceOfSatisfying
  • 準必須級
    • SoftAssertions
  • あったら便利
    • hasSize

終わりに

テストはできる限りシンプルにしたいので、使用するメソッドをできる限り減らしたいと考えてます。シンプルな方が、もしJUnit6が出て、AssertJが対応しなかった時に移行もしやすいですからね。もちろん、エラーメッセージが適切になるので、メソッドを覚えれば覚えるほどわかりやすいです。

もっといろいろと知りたい場合は、下記にAssertJのサンプルページもあるので、そちらから色々見てください。

参考記事

AssertJの公式ページ

github.com

AssertJのサンプルページ

github.com

AssertJ使い方メモ(Qiita)

qiita.com

assertEqualsとassertThatの比較記事

nainaistar.hatenablog.com


もしこの記事が役に立ったのであれば、はてぶ、Twitterでの記事の拡散、Twitterのフォローもよろしくお願いします。私の励みになります。

f:id:nainaistar:20201013111905p:plain