SYM's Tech Knowledge Index & Creation Records

「INPUT:OUTPUT=1:1以上」を掲げ構築する Tech Knowledge Stack and Index. by SYM@設計者足るため孤軍奮闘する IT Engineer.

Java Assertj + Mockito

Java Assertj + Mockito

Assertj

共通

  • 同値: isEqualTo
assertThat("Foo").isEqualTo("Foo");
assertThat("Foo").isNotEqualTo("Bar");
  • Null: isNull/isNotNull
assertThat(actual).isNull();
assertThat(actual).isNotNull();
assertThat(bar1).isSameAs(bar2);
assertThat(bar1).isNotSameAs(bar2);
assertThat(baz).isInstanceOf(Baz.class);
assertThat(qux).isInstanceOf(Baz.class).isInstanceOf(Qux.class);
assertThat(qux).isNotInstanceOf(Baz.class);
  • toString値: hasToString
assertThat(fooBar).hasToString("FooBar");  // toString()の値確認
  • 注釈: as
assertThat("Foo").as("AssertJ sample").isEqualTo("Bar");

文字列

  • 先頭/末尾一致
assertThat("FooBar").startsWith("Foo");
assertThat("FooBar").endsWith("Bar");
  • 大小無視の一致
assertThat("Foo").isEqualToIgnoringCase("FOO");
  • 空文字/Null
assertThat("").isEmpty();
assertThat(actual).isNullOrEmpty(); // null
assertThat("FooBarBaz").matches("F..B..B..").matches("F.*z");
  • 数字かどうか
assertThat("1234567890").containsOnlyDigits();
  • 行数確認
assertThat("foo\nbar\nbaz").hasLineCount(3);
assertThat("foo\r\nbar\r\nbaz").hasLineCount(3);

数値

  • 範囲(Between)
assertThat(7).isBetween(0, 9).isBetween(7, 7);
assertThat(7).isCloseTo(5, within(2)); // 5 ± 2 
  • 大なり/小なり
assertThat(7).isGreaterThan(6).isGreaterThanOrEqualTo(7);
assertThat(7).isLessThan(8).isLessThanOrEqualTo(7);

Collection(List/Set等)

  • hasSize: サイズ確認
assertThat(actuals).hasSize(4);
  • isEmpty: 空か
assertThat(actuals).isEmpty();
  • contains: 並び順は検証しない。含まれていればOK
List<String> actuals = Lists.newArrayList("Lucy", "Debit", "Anna", "Jack");
assertThat(actuals).contains("Lucy", "Anna");
  • containsOnly: 並び順は検証しない。全て含むならOK
List<String> actuals = Lists.newArrayList("Lucy", "Debit", "Anna", "Jack");
assertThat(actuals).containsOnly("Debit", "
Lucy", "Jack", "Anna");
  • containsSequence: 並び順を検証。件数確認しない
List<String> actuals = Lists.newArrayList("Lucy", "Debit", "Anna", "Jack");
assertThat(actuals).containsSequence("Lucy", "Debit");
  • containsSubSequence: 並び順を検証。件数確認しない。抜け漏れOK
List<String> actuals = Lists.newArrayList("Lucy", "Debit", "Anna", "Jack");
assertThat(actuals).containsSubsequence("Lucy", "Anna")
                   .containsSubsequence("Lucy", "Jack");
  • containsOnlyOnce: 含む かつ 重複なしならOK
List<String> actuals = Lists.newArrayList("Lucy", "Debit", "Anna", "Lucy");
assertThat(actuals).containsOnlyOnce("Debit", "Anna");
  • containsAnyOf: いずれか1つがある
assertThat(actuals).containsAny("Debit", "Anna");
  • extracting
// 特定フィールドのみ抽出
assertThat(list).extracting("name").containsExactly("佐藤","田中","鈴木");

Map

  • containsEntry
assertThat(actuals).containsEntry("Key1", 101)
                   .containsEntry("Key2", 202)
                   .doesNotContainEntry("Key9", 999);
  • containsKey
assertThat(actuals).containsKeys("Key2", "Key3")
                   .doesNotContainKey("Key9");
  • containsValue
assertThat(actuals).containsValues(202, 303)
                   .doesNotContainValue(999);
  • hasSize/isEmpty もある

Iterable関係

まとめて検証

  • allSatisfy: 全て満たす
assertThat(hobbits).allSatisfy(character -> {
  assertThat(character.getRace()).isEqualTo(HOBBIT);
  assertThat(character.getName()).isNotEqualTo("Sauron");
});
  • anySatisfy: いずれかを満たす
assertThat(hobbits).anySatisfy(character -> {
  assertThat(character.getRace()).isEqualTo(HOBBIT);
  assertThat(character.getName()).isEqualTo("Sam");
});
  • noneSatisfy: 全て満たさない
assertThat(hobbits).noneSatisfy(character -> assertThat(character.getRace()).isEqualTo(ELF));

特定の1つを検証

  • first/element/last: 最初/間/最後
Iterable<TolkienCharacter> hobbits = list(frodo, sam, pippin);
assertThat(hobbits).first().isEqualTo(frodo);
assertThat(hobbits).element(1).isEqualTo(sam);
assertThat(hobbits).last().isEqualTo(pippin);

単一要素

assertThat(babySimpsons).singleElement()
                        .isEqualTo("Maggie");

特定フィールドの抽出

assertThat(users).extracting((u) -> tuple(u.id, u.rank))
    .contains(tuple("abc", 10))
    .contains(tuple("def", 20));

assertThat(users).extracting("id", "rank")
    .contains(tuple("abc", 10))
    .contains(tuple("def", 20));
assertThat(params)
  .extracting(Param::key, Param::value)
  .containsExactlyInAnyOrder(
     tuple("analysisId", "abc"),
     tuple("projectId", "cde"));

assertAll (全て実行する)

失敗するものがあっても assertAll 内は全て検証される(ただし、結果が少々見にくい)

assertThat(persons.get(0))
  .satisfies(p -> assertAll(
    () -> assertThat(p.getName()).isEqualTo("SYM"),
    () -> assertThat(p.getAge()).isEqualTo(0),
    () -> assertThat(p.getJob()).isEqualTo("Engineer")
  ));

例外

  • 例外確認
assertThatThrownBy(() -> { throw new Exception("boom!"); })
    .isInstanceOf(Exception.class)  // 継承クラス含む
    .isExactlyInstanceOf(IOException.class)  // 一致
    .hasMessageContaining("boom");
  • 例外が発生しない
// どちらも同じ
assertThatNoException().isThrowBy(() -> System.out.println(""));
assertThatCode(() -> System.out.println("OK")).doesNotThrowAnyException();
  • 指定した例外発生確認
assertThatExceptionOfType(IOException.class)
    .isThrownBy(() -> { throw new IOException("boom!"); })
    .withMessage("%s!", "boom")
    .withMessageContaining("boom")
    .withNoCause();
  • 特定の例外確認
    • assertThatNullPointerException
    • assertThatIllegalArgumentException
    • assertThatIllegalStateException
    • assertThatIOException
assertThatIOException().isThrownBy(() -> { throw new IOException("boom!"); })
                       .withMessage("%s!", "boom")
                       .withMessageContaining("boom")
                       .withNoCause();

カスタムAssertion

var fooObj = new FooClass(); // 略
assertThat(fooObj).hasValue("bar");

public class FooClassAssert extends AbstractAssert<FooClassAssert, FooClass> {

  public FooClassAssert(FooClass actual) {  // 定型文
    super(actual, FooClassAssert.class);
  }

  public static FooClassAssert assertThat(FooClass actual) {  // 定型文
    return new FooClassAssert(actual);
  }

  public FooClassAssert hasValue(String key) {  // 独自Assertion
    isNotNull();
    if (actual.getValue(key) == null) {
      failWithMessage("エラーメッセージ");
    }

    return this;  // メソッドチェーンのために必須
  }
}

Mockito

  • mock 全体をモック化
var mockedService = new Mock(MessageService.class);
// モック
when(mockedService).getMessage(any()).thenReturn("モック化");
doReturn("モック化").when(mockedService).getMessage(any());

// 呼び出し回数検証
verify(mockedService, times(2)).getMessage(any());
  • spy 一部のみをモック化
var mockedService = new Spy(MessageService.class);
doReturn("モック化").when(mockedService).getMessage(any());

※テスト対象クラスをspyするのは避けた方が良い。責務が大きくなり図来ている兆候 (シングルトンクラスのメソッドのテスト無理やりできるが非推奨。場合による)

  • その他

mockito でコンストラクターの mock を使ったテストをしたい (Mockito 3.5.0 以降)

ref

以下も参考になりそう