2011年9月14日水曜日

[JUnit]assertThat用のMatcherを作成してみる

assertThat用のMatherはデフォルトで色々用意されているけど、拡張可能になっているので用途にあわせて独自に追加可能となっている。

基本的には、タイプセーフな「org.junit.internal.matchers.TypeSafeMatcher」を継承して拡張を行えばよい。
TypeSafeMatcherを拡張するポイントは、こんな感じ。
  • matchesSafely
    期待値と実際の値の比較を行うメソッド。
    比較結果をbooleanで返却してあげる。
  • describeTo
    期待値と実際の値が異なる場合の実際のメッセージに表示する値を生成する。(下の画像の部分)

サンプル

このサンプルは、実際の値がjava.util.Dateの場合に、期待値に8桁の文字列を指定出来るようにするMatcher。
実際にisを使うと期待値と実際の値はタイプが一致していないといけないので、困難があると決行便利だったりする。

    @Test
    public void testGetDate() {
        // 実際の値はDateだが、期待値には8桁の文字列日付を指定する。
        assertThat(Hoge.getDate(), DateMatcher.isDate("20110910"));
    }

    /**
     * Date型と8桁の文字列をassertするクラス。
     * org.junit.internal.matchers.TypeSafeMatcherを継承して機能拡張する。
     */
    private static class DateMatcher extends TypeSafeMatcher {

        private String expected;
        private Date expectedDate;

        public DateMatcher(String expected) {
            this.expected = expected;
            if (expected == null) {
                return;
            }
            // 期待値が有効な日付かのチェックを事前に行う。
            SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
            try {
                expectedDate = yyyyMMdd.parse(expected);
            } catch (ParseException e) {
                throw new IllegalArgumentException("8桁の有効な日付を指定してください。", e);
            }
        }

        /**
         * assertThatの期待値に指定するMatcherのファクトリメソッド。
         * @param date 期待値(8桁の有効な日付)
         * @return Matcher
         */
        public static DateMatcher isDate(String date) {
            return new DateMatcher(date);
        }

        /**
         * メッセージの構築。
         * 期待値をDateに変換してメッセージに追加する。(時間部分は差分として出ちゃうけど、めんどいのでこの辺は適当)
         * @param description
         */
        public void describeTo(Description description) {
            if (expected == null) {
                description.appendText(expected);
            } else {
                description.appendValue(expectedDate);
            }
        }

        /**
         * 実際の値との比較を行う。
         * @param item 実際の値
         * @return 一致している場合はtrue
         */
        @Override
        public boolean matchesSafely(Date item) {
            if (item == null && expected == null)  {
                return true;
            }
            // 実際の値を8桁の文字列日付に変換して比較を行う。
            SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
            String actual = yyyyMMdd.format(item);
            return actual.equals(expected);
        }
    }