31
24

More than 5 years have passed since last update.

rspec-json_matcherでJSONの検証を自由自在に行う

Last updated at Posted at 2016-03-30

概要

RSpecを用いてJSONの検証をする際に使える rspec-json_matcher の隠された(?)能力を紹介しましょう

rspec-json_matcherとは

expect('{"a": "b", "c": "d" }').to be_json_as("a" => "b", "c" => "d")

このように、JSON文字列を直接検証できます

でも、こんな時どうする?

下記のような2つのJSONがあったとき、root の配列の順序を気にすること無く、内包している値のみに注目して検証するにはどうしたらいいでしょうか?

{
  "root": [
    {"a": 1},
    {"b": 2}
  ]
}
{
  "root": [
    {"b": 2},
    {"a": 1}
  ]
}

ちなみに、これでは2番目で失敗します (配列内の順番も検証してしまうため)

let(:spec) { {"root" => [{"a" => 1}, {"b" => 2}]} }
expect('{"root":[{"a":1},{"b":2}]}').to be_json_as(spec)
#=> pass
expect('{"root":[{"b":2},{"a":1}]}').to be_json_as(spec)
#=> fail

解決策

rspec-json_matcherは、RSpecのmatcherをそのまま書くことができます
今回の例では contain_exactly を使うのが良さそうです

let(:spec) { {"root" => contain_exactly({"a" => 1}, {"b" => 2})} }
expect('{"root":[{"a":1},{"b":2}]}').to be_json_as(spec)
#=> pass
expect('{"root":[{"b":2},{"a":1}]}').to be_json_as(spec)
#=> pass

またはマッチャ合成機能(and/or)とincludeを組み合わせても同じことが実現できます

let(:spec) { {"root" => include({"a" => 1}).and include({"b" => 2})} }
expect('{"root":[{"a":1},{"b":2}]}').to be_json_as(spec)
#=> pass
expect('{"root":[{"b":2},{"a":1}]}').to be_json_as(spec)
#=> pass

解説

rspec-json_matcherの例にも書いてあるとおり、値の部分には true/falseだけでなく、StringやHashといったオブジェクトタイプの他、正規表現も書くことができます

これの応用です。すなわち値に書かれた内容はそのままRSpecのmatcherとして評価されることを利用しています

これが分かっていれば、自由自在にJSON検証が可能です

注意

残念なことに、キーの方には適用(評価)できません

expect('{"root":[{"a":1},{"b":2}]}').to be_json_as( {start_with("r") => Array} )
#=> fail
#||        reason: [
#||            [0] "root",
#||            [1] "#<RSpec::Matchers::BuiltIn::StartWith:0x0055e05b3cc0d0>"
#||         ]

検証コード

RSpec 3.4.0

Gemfile
source "https://rubygems.org"
gem "rspec", require: "rspec/core"
gem "rspec-json_matcher", require: "rspec/json_matcher"
gem "rake"
spec/spec_helper.rb
require "bundler"
Bundler.require
RSpec.configuration.include RSpec::JsonMatcher
spec/json_spec.rb
describe "json" do
  let(:spec) {
    {"root" => contain_exactly({"a" => 1}, {"b" => 2})}
  }

  it "order is a to b" do
    expect('{"root":[{"a":1},{"b":2}]}').to be_json_as(spec)
  end

  it "order is b to a" do
    expect('{"root":[{"b":2},{"a":1}]}').to be_json_as(spec)
  end
end

実行結果

$ bundle exec rspec --format d

json
  order is a to b
  order is b to a

Finished in 0.00201 seconds (files took 0.09972 seconds to load)
2 examples, 0 failures

参考資料

あとがき

REST API開発が捗ります
ってか、APIのチェックの為だけにRSpec使っても良いくらいです (=> やってました)

31
24
1

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
31
24