Java
JSON
JUnit
hamcrest

JSON 文字列を JUnit / hamcrest でアサーションしたいときは hamcrest-json を使うと捗る

More than 3 years have passed since last update.

ジャバで JUnit 使っていて、JSON 文字列の検証をしなきゃならなくなったときは hamcrest-json を使うと捗るよ、という話。

JUnit での JSON 文字列のアサーション

JSON はその定義上、オブジェクトのメンバの定義順序にあまり意味を持たせることはなくて、本来なら

{
  "foo": "bar",
  "hoge": "fuga"
}

{
  "hoge": "fuga",
  "foo": "bar"
}

は同一視してあげたいところなんだけど、以下のように JUnit / hamcrest で単純に文字列の比較をしてしまうと当然アサーションに失敗してしまうわけでして、これを解消するのがちょっと悩ましい。

    @Test
    public void これではアサーションに失敗してしまう() {
        String expected = "{\"foo\": \"bar\", \"hoge\": \"piyo\"}";
        String actual = "{\"hoge\": \"piyo\", \"foo\": \"bar\"}";

        assertThat(
                actual,
                is(expected));
    }

「そんなの、Jackson とか使って JSON 文字列をいったん Map / List などの Java のオブジェクトにデシリアライズすりゃ片付くことでしょ?」ということをいう人もいるかもしれない。確かにそれもそうなのだが、ちょっと複雑な構造の JSON においてアサーションに失敗した場合、「いったいどこが間違っているのか?」を確認するのが面倒だったりする。

でもそんなときは、hamcrest-json を使えばだいたいすべて解決します☆

hamcrest-json の使い方 (サンプルコード)

使い方を一つ一つ説明するのもアレなので、サンプルコードをご覧 or 実行してみてください。

package biz.k11i.demo;

import org.junit.Test;

import static org.junit.Assert.assertThat;
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;

/**
 * hamcrest-json の利用デモ。
 * <p>Gradle をお使いの方なら、以下のように記述すれば OK</p>
 * <pre>
 * dependencies {
 *     testCompile group: 'junit', name: 'junit', version: '4.11'
 *     testCompile group: 'uk.co.datumedge', name: 'hamcrest-json', version: '0.2'
 * }
 * </pre>
 *
 * @author KOMIYA Atsushi
 */
public class HamcrestJsonDemo {
    @Test
    public void オブジェクト内のメンバの順序が異なっていても大丈夫() {
        String expected = "{\"foo\": \"bar\", \"hoge\": \"piyo\"}";
        String actual = "{\"hoge\": \"piyo\", \"foo\": \"bar\"}";

        assertThat(
                actual,
                sameJSONAs(expected));
    }

    @Test
    public void アサーションに失敗した場合はどこが異なるのかを明確にしてくれます() {
        String expected = "{\"fuga\": \"fugafuga\"}";
        String actual = "{\"hoge\": \"piyo\", \"foo\": \"bar\"}";

        assertThat(
                actual,
                sameJSONAs(expected));

        /*
         * 以下の出力が得られます。
         *
         * Expected: "{\"fuga\": \"fugafuga\"}"
         * but:
         * Expected: fuga
         *      but none found
         *  ;
         * Unexpected: foo
         *  ;
         * Unexpected: hoge
         */
    }

    @Test
    public void 余計なメンバが存在してもそれを許容する包容力も持ち合わせています() {
        String expected = "{\"hoge\": \"piyo\"}";
        String actual = "{\"hoge\": \"piyo\", \"foo\": \"bar\"}";

        // SameJSONAs#allowingExtraUnexpectedFields() を呼び出すと、余計なメンバを許容してくれます
        assertThat(
                actual,
                sameJSONAs(expected)
                        .allowingExtraUnexpectedFields());
    }

    @Test
    public void 配列の順序を無視して比較することもできます() {
        String expected = "[1, 1, 2, 3, 5, 8]";
        String actual = "[8, 5, 3, 2, 1, 1]";

        // SameJSONAs#allowingAnyArrayOrdering() を呼び出すと、配列の順序を無視してくれます
        assertThat(
                actual,
                sameJSONAs(expected)
                        .allowingAnyArrayOrdering());
    }
}

hamcrest-json、デキる子ですね!