16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

エムスリーAdvent Calendar 2016

Day 4

JUnitでMatcher活用のすすめ

Posted at
1 / 11

JUnitでテストを書く

import static org.hamcrest.Matchers.is

assertThat("string", is("string"));

この is()がMatcherです。
たくさんの種類があるので覚えておくとテストが楽になります


Matcher活用の利点

  • コレクション系のassertがシンプルに書ける
  • assert失敗時のメッセージがわかりやすくなる
  • インスタンスを部分的に検証できる

使い方

hamcrestのライブラリ
「org.hamcrest:hamcrest-all:1.3」を使います

pom.xml
<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-all</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>

活かせていない書き方

List<String> list = 
    Arrays.asList("abc", "def", "ghi");
for (String s : list) {
    assertThat(s.startsWith("zx"), is(true));
}
# 失敗メッセージ
java.lang.AssertionError: 
Expected: is <true>
     got: <false>
  • Listの要素を自前でループしている
  • 失敗メッセージだけでは何がどうダメだったのかわからない

Matcherを活用した場合

List<String> list =
    Arrays.asList("abc", "def", "ghi");

assertThat(list, everyItem(startsWith("zx")));
Expected: every item is 
          a string starting with "zx"
     got: <[abc, def, ghi]>
  • ループが不要になる
  • 失敗メッセージでコレクション全体の内容がわかる

インスタンスの一部を検証

class KeyValue {
  private String key;
  private String value;
  getter / setter ...
  // toStringをわかりやすくしておくのがポイント
  public String toString() {
    return ReflectionToStringBuilder(this);
  }
}

KeyValue kv = new KeyValue("k1", "v1");

// 以下は同じ意味
assertThat(kv.getKey(), is("k1"));

assertThat(kv, hasProperty("key", is("k1")));
java.lang.AssertionError: 
Expected: hasProperty("key", is "xyz")
     got: <key=k, value=v>
  • インスタンスの部分的なプロパティだけを検証できる
  • 失敗エラーにオブジェクト全体の内容が出力される

Matcherの一部を紹介

List<KeyValue> list = Arrays.asList(
        new KeyValue("k1", "v1"),
        new KeyValue("k2", "v2"),
        new KeyValue("k3", "v3")
);

// hasItems: コレクション内に指定の要素が存在するかどうか
assertThat(list, hasItems(
        hasProperty("key", is("k3")),
        hasProperty("key", startWith("k2"))
));

// contains: 指定した要素すべてが過不足なく順序通りにマッチすること
assertThat(list, contains(
        hasProperty("key",   is("k1")),
        hasProperty("value", is("v2")),
        hasProperty("key",   is("k3"))
));

でも残念なことも...

JUnitの最新版 4.11以上 にすると、失敗メッセージがHuman Readableに!
でも、assert対象オブジェクトの内容がわかりにくくなってしまい残念...

java.lang.AssertionError: 
Expected: (a collection containing 
           a string starting with "zx")
     but: a collection containing
          a string starting with "zx" 
          was "abc", was "def", was "ghi"

java.lang.AssertionError: 
Expected: iterable containing [hasProperty("key", is "k1")]
     but: item 1: property 'key' was "k2"
  • 個人的には JUnit 4.10 が使いやすい

よく困ること

// リストのサイズが3以上 かつ 全てのgetKey() が "k"から始まる
assertThat(/* List<KeyValue> */ keyValueList, 
    allOf(
        hasSize(greaterThanOrEqualTo(3)),
        everyItem(hasProperty("key", startsWith("k")))
    )
);

上記はコンパイルできない。

// 修正版
// Matcherが意味する型を明示しないと型推論ができない
assertThat(/* List<KeyValue> */ keyValueList, 
    Matchers.<List<KeyValue>>allOf(
        hasSize(greaterThanOrEqualTo(3)),
        everyItem(hasProperty("key", startsWith("k")))
    )
);

参考

実際のMatcherは以下を参考にしてください

16
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?