LoginSignup
3
2

More than 5 years have passed since last update.

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

Posted at

概要

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

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

既存のものと自作のものの比較

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

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

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

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

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

使い方

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

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

を継承して

jsonA must equalToJson(jsonB)

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

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

3
2
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
3
2