hamcrest の Matchers 詳細

blog1.mammb.com

では CoreMatchers についてでしたが、こちらでは org.hamcrest.Matchers についてまとめます。

org.hamcrest.Matchers

JUnit についてくるのは org.hamcrest.CoreMatchers で基本的な Matcher が提供されています。org.hamcrest.Matchers は CoreMatchers を機能拡張したものとなってます。CoreMatchers にあるメソッドは、Matchers にもあります。

  • hamcrest-core  − org.hamcrest.Matchers が入ってる
  • hamcrest-library  − org.hamcrest.Matchers が入ってる


hamcrest-library は以下のようなパッケージ構成となっており、各用途に応じた Matcher が定義されています。

  • org.hamcrest.beans
  • org.hamcrest.collection
  • org.hamcrest.number
  • org.hamcrest.object
  • org.hamcrest.text
  • org.hamcrest.xml

なお、ここでは hamcrest 1.3.0 をベースとしています。また import は static import しているものとします。

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

allOf() と anyOf() で AND 条件と OR 条件を指定する

allOf() では Matcher を複数指定して AND条件の Matcher を作ります。

String actual = "hoge";
assertThat(actual, is(allOf(startsWith("h"), endsWith("e"))));
assertThat(actual, is(allOf(startsWith("h"), containsString("og"), endsWith("e"))));


anyOf() では Matcher を複数指定して OR条件の Matcher を作ります。

String actual = "hoge";
assertThat(actual, is(anyOf(startsWith("h"), endsWith("zz"))));
assertThat(actual, is(anyOf(startsWith("zz"), containsString("og"), endsWith("zz"))));


以下のように Iterable で Matcher を指定することもきます。

Iterable<Matcher<? super String>> matchers = new ArrayList<Matcher<? super String>>() {{
        add(startsWith("h")); add(containsString("og")); add(endsWith("e"));
    }};
assertThat(actual, is(allOf(matchers)));
Iterable<Matcher<? super String>> matchers = new ArrayList<Matcher<? super String>>() {{
        add(startsWith("zz")); add(containsString("og")); add(endsWith("zz"));
    }};
assertThat(actual, is(anyOf(matchers)));

both() と either() で AND 条件と OR 条件をあつかう

both() と either() でも AND 条件と OR 条件の Matcher を構築できます。

assertThat("hoge", both(startsWith("h")).and(endsWith("e")));
assertThat("hoge", either(endsWith("e")).or(endsWith("z")));

both().or() ともできますし、either().and() ともできます。自然な英語になるような記述を選ぶことができます。

どれか を指定する isIn() と isOneOf()

Matcher の引数にコレクションを指定する isIn() 可変長引数を取る isOneOf() があります。

Collection<String> collection = Arrays.asList("foo1", "foo2", "foo3");
assertThat("foo2", isIn(collection));
assertThat("foo2", isOneOf("foo1", "foo2", "foo3"));

型とインスタンスを扱う Matcher

型のマッチングには isA() を使います。

assertThat("hoge", isA(String.class));
assertThat("hoge", is(any(String.class)));
assertThat("hoge", is(instanceOf(String.class)));

any はモックライブラリなどで引数はXX型のクラスならどんなクラスでも良いという指定を行う場合などで利用されます。instanceOf は any のエイリアスとなっています。


インスタンスの同一性は sameInstance() を使います。

Boolean b = true;
assertThat(b, is(sameInstance(Boolean.TRUE)));

数値を比較する Matcher

ある範囲を持った数値には closeTo が使えます。以下では 10±0.3 の期待値の範囲内かどうかを検証しています。

assertThat(10.2, is(closeTo(10, 0.3)));


greaterThan() と lessThan() により不等式を扱えます。

assertThat(10, is(greaterThan(5)));// value > expected
assertThat(10, is(greaterThanOrEqualTo(10)));// value >= expected

assertThat(10, is(lessThan(15)));// value < expected
assertThat(10, is(lessThanOrEqualTo(10)));// value <= expected


compareTo で比較を行うのが、以下のメソッドたちです。

BigDecimal actual = BigDecimal.valueOf(10.0);		
assertThat(actual, is(comparesEqualTo(BigDecimal.valueOf(10))));     // value =  expected

assertThat(actual, is(greaterThan(BigDecimal.valueOf(5))));          // value >  expected
assertThat(actual, is(greaterThanOrEqualTo(BigDecimal.valueOf(5)))); // value >= expected

assertThat(actual, is(lessThan(BigDecimal.valueOf(15))));            // value <  expected
assertThat(actual, is(lessThanOrEqualTo(BigDecimal.valueOf(15))));   // value <= expected

文字列を比較する Matcher

大文字 小文字の違いを無視するのが equalToIgnoringCase()、加えてホワイトスペースも無視するのが equalToIgnoringWhiteSpace() です。

assertThat("STRING", is(equalToIgnoringCase("String")));
assertThat(" STRING ", is(equalToIgnoringWhiteSpace("String")));


toString の内容を明示的に比較するのが hasToString となります。

assertThat(Collections.emptyList(), is(hasToString("[]")));
assertThat(new Object(), is(hasToString(startsWith("java.lang.Object@"))));

何にでもマッチする anything() と 詳細記述を追加する describedAs()

anything() は何にでもマッチします。補足情報などの記述を追加するには describedAs() が用意されています。

assertThat("hoge", anything());
assertThat("hoge", anything("always evaluates to true."));

assertThat("hoge", describedAs("end with e ", endsWith("e")));
assertThat("hoge", describedAs("end with %0 ", endsWith("e"), "「e」"));
  // Expected: end with "「e」" の詳細表示となる

コレクション(Iterable)を扱う Matcher

hasItem() は引数で指定したものがコレクション中に存在する場合にマッチする Matcher です。引数を複数指定できるのが hasItems() となります。

Iterable<String> actual = Arrays.asList("foo1", "foo2", "foo3");
assertThat(actual, hasItem("foo1"));
assertThat(actual, hasItems("foo1", "foo2"));


以下のように Matcher を引数として指定することもできます。

Iterable<String> actual = Arrays.asList("foo1", "foo2", "foo3");
assertThat(actual, hasItem(endsWith("2")));
assertThat(actual, hasItems(endsWith("1"), endsWith("3")));


コレクション中の全ての要素について Matcher を適用する everyItem() もあります。

Iterable<String> actual = Arrays.asList("foo1", "foo2");
assertThat(actual, is(everyItem(startsWith("f"))));


contains() は指定した順番通り、containsInAnyOrder() は順番については気にしません。

Iterable<String> actual = Arrays.asList("foo1", "foo2", "foo3");
assertThat(actual, is(contains("foo1", "foo2", "foo3")));
assertThat(actual, is(containsInAnyOrder("foo2", "foo1", "foo3")));


コレクションのサイズを見るには hasSize() や、empty(), emptyIterable()があります。

Collection<String> actual = Arrays.asList("foo1", "foo2", "foo3");
assertThat(actual, hasSize(3));
assertThat(actual, hasSize(lessThan(4)));

assertThat(Collections.emptyList(), is(empty()));         // Collectionを対象
assertThat(Collections.emptyList(), is(emptyIterable())); // Iterableを対象

マップを扱う Matcher

hasEntry()、hasKey()、hasValue() は読んだ通りの期待した動きをします。

Map<Integer,String> map = new HashMap<Integer, String>(){{
    put(1, "val1"); put(2, "val2"); put(3, "val3");
}};
		
assertThat(map, hasEntry(2, "val2"));
assertThat(map, hasKey(2));
assertThat(map, hasValue("val2"));

配列を扱う Matcher

配列を扱うために array() があります。

// 各要素を照合
String[] actual = {"foo1", "foo2", "foo3"};
assertThat(actual, is(array(is("foo1"), anything(), is("foo3"))));


以下も配列を扱う Matcher になります。

assertThat(actual, hasItemInArray("foo2"));
assertThat(actual, hasItemInArray(endsWith("2")));

// 順序を考慮する
assertThat(actual, arrayContaining("foo1", "foo2", "foo3"));
// 順序を考慮しない
assertThat(actual, arrayContainingInAnyOrder("foo2", "foo1", "foo3"));

assertThat(actual, is(arrayWithSize(3)));
assertThat(actual, is(arrayWithSize(lessThan(4))));
assertThat(actual, is(not(emptyArray())));

Bean のプロパティを扱う Matcher

hasProperty() ではプロパティ名を指定します。以下の例では無理に Date を使ってますが、任意の Bean を指定します。

Date actual = new Date(1);
assertThat(actual, hasProperty("time"));
assertThat(actual, hasProperty("time", is(1L)));

それぞれ、指定したプロパティがあるか? 指定したプロパティの値が指定値となっているか? の asser となっています。


プロパティの値を比較する samePropertyValuesAs() もあります。

assertThat(actual, is(samePropertyValuesAs(new Date(1))));

EventObject を扱う Matcher

利用頻度はあまり高くないかもですが、EventObject 用の Matcher もあります。

// イベントの発生元を比較(ここではサンプルとしてthisがイベントソースとした)
java.util.EventObject actual = new ActionEvent(this, 0, "a");
assertThat(actual, is(eventFrom(this)));
assertThat(actual, is(eventFrom(ActionEvent.class, this)));

eventFrom() はイベントの発生元が一致するかの assert で使用します。

XML を扱う Matcher

XML ドキュメントを hasXPath 指定で扱える Matcher もあります。

Document document = DocumentBuilderFactory.newInstance()
    .newDocumentBuilder()
    .getDOMImplementation()
    .createDocument("", "root", null);

    Element root = document.getDocumentElement();
    Element elm = document.createElement("elm");
    elm.appendChild(document.createTextNode("node"));
    root.appendChild(elm);

    assertThat(document, hasXPath("/root/elm"));
   assertThat(document, hasXPath("/root/elm", is("node")));


これでだいたい網羅でしょう。