動的な値を含むJSON文字列をテストするための json-fuzzy-match を作った
APIなどのテストで、JSONが意図した形式・値になっているかチェックしたいことがあります。
文字列として比較してチェックする場合、JSONに含まれる空白がちょっと変わっただけでエラーになりますし、チェック対象のJSON文字列が整形されていないとテストの可読性が落ちます。さらに自動生成されるIDや更新日時など、動的な値が含まれていると使えません*1。
JSON文字列をパースしてから比較する場合、Jacksonの ObjectMapper#readValue()
のような気の利いたデシリアライズ機能を使うと、本当にJSON文字列として意図した形になっているか自信が持てません。一方で、 ObjectMapper#readTree()
のように木構造としてパースする機能を使うと、記述が冗長になりがちで面倒です。テストコードも読みやすくありません。
json-fuzzy-match
そこで、JVM系言語においてJSON文字列をいい感じに比較できるアサーションライブラリ「json-fuzzy-match」を作りました。スペースや改行などのフォーマットの違いを無視して比較できるだけでなく、 #string
#number
#uuid
のようなマーカーを使って、動的な値を含むJSONをそのまま比較できます。
例えばテスト対象とする response.content
が次のようなJSON文字列だとします。
{ "id": "2c0a9fd7-be2c-4bc2-b134-acc3fa13d400", // 自動生成されたUUID "title": "Example Book", "price": "9.99", "currency": "USD", "amount": 10, "timestamp": "2019-09-25T13:34:17Z" // 動的なタイムスタンプ }
json-fuzzy-matchを使うと、次のように比較できます*2。UUIDやタイムスタンプのような動的な項目にはマーカーを指定しているので、形式が合っていればテストが通ります。
JsonStringAssert.assertThat(response.content).jsonMatches(""" { "id": "#uuid", "title": "Example Book", "price": "9.99", "currency": "USD", "amount": 10, "timestamp": "#string" } """.trimIndent())
インストール方法やマーカーのリファレンスなど、詳しくはGitHubのリポジトリを参照してください。
謝辞
json-fuzzy-matchは Karate というテスト自動化ツールの機能の一つである Fuzzy Matching をJUnitやAssertJから使いやすいようにラップしただけのライブラリです。使えるマーカーもKarateのそのままです。
JSONのテストを書くのは面倒で、日頃から良い方法がないか考えていた中でKarateのFuzzy Matchingに出会いました。このような便利なツールがOSSとして公開されていたことで、JSONのテストを簡単に書けるようになったので、Karateの開発者には非常に感謝しています。この場を借りてお礼申し上げます。
最後に
しばらく勤務先で使っていますが、それなりに便利だと思うので、良ければぜひ使ってみてください。改善点などあれば、IssueやPull Requestなどいただけると嬉しいです。
あるいは、JSONをテストするためのもっと良い方法をご存知であれば、ぜひ教えていただけるとありがたいです。