Play FrameworkのSpecs2でJsonを比較するMatcherを自作して見やすくした

# 概要
Play FrameworkのSpecs2でJsonを比較するテストを書いていると、エラーメッセージが分かりにくくて、げんなりした経験ありますでしょうか。
一致しないことはわかるけど、どこの値が一致しないなどがわかりにくかったので、自作してみました。

今回使ったソースは、
https://github.com/2KB/json-matcher
に公開しています。

# 既存のものと自作のものの比較
既存のものは、こんな感じでJsonの比較をすると

```scala
val jsonA = Json.parse(
  """
    {
      "string_field" : "string",
      "number_field" : 100,
      "boolean_field" : false,
      "null_field" : null,
      "array_field" : ["string", 100, false, null],
      "object_field" : {"xxx_field" : "xxx", "yyy_field" : 100}
    }
  """)

// jsonAから、フィールドの順番を変えつつ、xxx_fieldの値を変えている
val jsonB = Json.parse(
  """
    {
      "number_field" : 100,
      "string_field" : "string",
      "boolean_field" : false,
      "null_field" : null,
      "object_field" : {"xxx_field" : "zzz", "yyy_field" : 100},
      "array_field" : ["string", 100, false, null]
    }
  """)

jsonA must be_==(jsonB)
```

こんなエラーメッセージがでます。

```
[error]    '{"string_field":"string","number_field":100,"boolean_field":false,"null_field":null,"array_field":["string",100,false,null],"object_field":{"xxx_field":"xxx","yyy_field":100}}'
[error]
[error]     is not equal to
[error]
[error]    '{"number_field":100,"string_field":"string","boolean_field":false,"null_field":null,"object_field":{"xxx_field":"zzz","yyy_field":100},"array_field":["string",100,false,null]}' (CompareJsonMatchErrorMessageSpec.scala:14)
[error] Actual:   {"[string]_f...":[]"s...ng[","number]_f...":[100],"...,"[array]_f...":[]"[string",100,fa]l[se,null][object]_f...":[{]"[xxx]_f...":[]"[xxx]",["yyy_]f[ield":100}]}
[error] Expected: {"[number]_f...":[100,]"s...ng[]_f...":["string"],"...,"[object]_f...":[{]"[xxx_fie]l[d":"zzz"],"[yyy]_f...":[100},]"[array]_f...":[]"[string]",[100,]f[alse,null
```

![2016050518382348d.jpg](https://qiita-image-store.s3.amazonaws.com/0/36837/7e2b526f-89d3-6520-bf14-517e63fa8328.jpeg)


ちょっとどこ間違っているのかわからないですね。
しかも、一致しているかどうかに関しては、フィールドの順番無視するくせに、どこで間違っているかのメッセージでは、順番そのままで比較するという、残念感。

AとBで違いがあるのは、xxx_field の値だけですので、そこがぱっとわかるようになってほしいものです。

自作したやつだと、エラーメッセージはこんな感じになります。


```
jsonA must equalToJson(jsonB)
```


```
[error]    Each json not match.
[error]
[error]    Path     : object_field->xxx_field
[error]    Actual   : "xxx"
[error]    Expected : "zzz"
[error]    -----
[error]    Actual detail   :
[error]    {
[error]      "array_field" : [ "string", 100, false, null ],
[error]      "boolean_field" : false,
[error]      "null_field" : null,
[error]      "number_field" : 100,
[error]      "object_field" : {
[error]        "xxx_field" : "xxx",
[error]        "yyy_field" : 100
[error]      },
[error]      "string_field" : "string"
[error]    }
[error]
[error]    Expected detail :
[error]    {
[error]      "array_field" : [ "string", 100, false, null ],
[error]      "boolean_field" : false,
[error]      "null_field" : null,
[error]      "number_field" : 100,
[error]      "object_field" : {
[error]        "xxx_field" : "zzz",
[error]        "yyy_field" : 100
[error]      },
[error]      "string_field" : "string"
[error]    }
[error]    -----
[error]
[error]    at (CompareJsonMatchErrorMessageSpec.scala:19)
```

![20160505183821fde.jpg](https://qiita-image-store.s3.amazonaws.com/0/36837/4665236d-072b-2495-79bb-0525905722e0.jpeg)

どこが間違っているかはわかりやすくなったかなと思います。

# 使い方
まずはSpecs2の設定が必要です。build.sbtに、「specs2 % Test」のライブラリを追加しましょう

```scala
libraryDependencies ++= Seq(
  ...
  specs2 % Test
)
```

そして、今回作成したMatcher
https://github.com/2KB/json-matcher/blob/master/test/matchers/JsonMatcher.scala
をダウンロードしてプロジェクトに配置し、

https://github.com/2KB/json-matcher/blob/master/test/CompareJsonMatchErrorMessageSpec.scala
でやっているように、今回作成した

JsonMatcher 

を継承して

```scala
jsonA must equalToJson(jsonB)
```

のようにして比較してください。
比較する値は、JsValue型です。
下記のようにJsonの文字列をパースすると，JsValue型になります。

```scala
import play.api.libs.json.Json
val jsonVal = Json.parse("""{"xxx" : "hogehoge"}""")
```

Play Framework以外のSpecs2でも使えるとは思いますが、
その場合はPlay-JSONライブラリも必要かと思います。
https://mvnrepository.com/artifact/com.typesafe.play/play-json_2.11

# その他説明など

## Matcherの作り方について
今回作ったMatcherは
https://github.com/2KB/json-matcher/blob/master/test/matchers/JsonMatcher.scala
ですが、この作り方が一般的かどうかなどは正直よく分かりません・・・。

Matcherの作り方はあまり情報がなく、情報があっても実際に作ってみると、行数がうまくとれないなどの問題があり、
試行錯誤しているうちになんとなくできたものになりますので、もし作り方に詳しい人がいて、ちょっとこれおかしいんじゃない？
というのがあれば是非ツッコミ頂ければ幸いです。


## 比較方法詳細
- オブジェクト型のフィールドの順番が違っても、エラーにしない
- 配列型の中身の順番が違った場合は、エラーにする
- フィールドはフィールド名の昇順でソートする
- 最初に見つかったエラーのみを表示する

となっています。

## ほんとに比較結果あっているの？
一応テストは作りました。
多分大丈夫だとは思いますが、ミス等あればツッコミよろしくお願い致します。
https://github.com/2KB/json-matcher/blob/master/test/matchers/JsonMatcherSpec.scala