RSpec::ComposableJSONMatchers
RSpec::ComposableJSONMatchers provides be_json matcher,
which lets you express expected structures on JSON strings
with the power of RSpec's
built-in matchers
and
composable matchers.
json = '{ "foo": 1, "bar": 2 }'
expect(json).to be_json a_kind_of(Hash)
expect(json).to be_json matching(foo: 1, bar: a_kind_of(Integer))
expect(json).to be_json including(foo: 1)Installation
Adding rspec-composable_json_matchers dependency
Add this line to your Gemfile:
gem 'rspec-composable_json_matchers'And then run:
$ bundle installEnabling be_json matcher
To make the be_json matcher available in every example,
add the following line to your spec/spec_helper.rb:
# spec/spec_helper.rb
require 'rspec/composable_json_matchers/setup'Or if you prefer more explicit way, add the following snippet:
# spec/spec_helper.rb
require 'rspec/composable_json_matchers'
RSpec.configure do |config|
config.include RSpec::ComposableJSONMatchers
endIf you want to enable the be_json matcher only specific examples rather than every example,
include the RSpec::ComposableJSONMatchers in the example groups:
# spec/something_spec.rb
require 'rspec/composable_json_matchers'
describe 'something' do
include RSpec::ComposableJSONMatchers
endUsage
The be_json matcher takes another matcher
or a JSON value (hash, array, numeric, string, true, false, or nil).
When a matcher is given, it passes if actual string can be decoded as JSON and the decoded value passes the given matcher:
expect('{ "foo": 1, "bar": 2 }').to be_json a_kind_of(Hash)
expect('{ "foo": 1, "bar": 2 }').to be_json matching(foo: 1, bar: 2)
expect('{ "foo": 1, "bar": 2 }').to be_json including(foo: 1)
expect('["foo", "bar"]').to be_json a_kind_of(Array)
expect('["foo", "bar"]').to be_json matching(['foo', 'bar'])
expect('["foo", "bar"]').to be_json including('foo')
expect('null').to be_json(nil)When a JSON value is given,
it's handled as be_json matching(value) (matching is an alias of the match matcher):
# Equivalents
expect('{ "foo": 1, "bar": 2 }').to be_json(foo: 1, bar: 2)
expect('{ "foo": 1, "bar": 2 }').to be_json matching(foo: 1, bar: 2)You can compose matchers via given matchers:
expect('{ "foo": 1, "bar": 2 }').to be_json matching(
foo: a_kind_of(Integer),
bar: a_kind_of(Integer)
)
expect('{ "foo": 1, "bar": 2 }').to be_json including(foo: a_kind_of(Integer))For more practical example, see
spec/example_spec.rb
for the GitHub Meta API.
Combinations with built-in matchers
Since decoded JSON is a hash or an array in most cases, you may want to use any of the following built-in matchers.
Note that you can always use the match matcher (internally uses #===)
instead of the eq matcher (internally uses #==),
because there's no object that is parsed from JSON and behaves differently between #== and #===.
Of course, any other custom matchers can also be used.
Also, using the built-in matcher aliases is recommended since it reads well:
# Equivalents
expect('{ "a": "foo" }').to be_json matching(a: a_string_including('oo')) # Matcher aliases
expect('{ "a": "foo" }').to be_json match(a: include('oo')) # Original matcher namesmatching
- Alias of
matchmatcher - Supported structure: Hash and Array
- Accepts matchers as arguments: Yes
expect('{ "foo": 1, "bar": 2 }').to be_json matching(foo: 1, bar: a_kind_of(Integer))
expect('["foo", "bar"]').to be_json matching(['foo', a_string_starting_with('b')])including
- Alias of
includematcher - Supported structure: Hash and Array
- Accepts matchers as arguments: Yes
expect('{ "foo": 1, "bar": 2 }').to be_json including(foo: 1)
expect('["foo", "bar"]').to be_json including('foo')all
-
allmatcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json all be_a(String)containing_exactly
- Alias of
contain_exactlymatcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json containing_exactly('bar', 'foo')starting_with
- Alias of
start_withmatcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json starting_with('foo')ending_with
- Alias of
end_withmatcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json ending_with('bar')having_attributes
- Alias of
have_attributesmatcher - Supported structure: Hash and Array
- Accepts matchers as arguments: Yes
expect('{ "foo": 1, "bar": 2 }').to be_json having_attributes(keys: [:foo, :bar])
expect('["foo", "bar"]').to be_json having_attributes(size: 2)a_kind_of
- Alias of
be_a_kind_ofmatcher - Supported structure: Hash and Array
- Accepts matchers as arguments: No
expect('{}').to be_json a_kind_of(Hash)
expect('[]').to be_json a_kind_of(Array)Configuration
The be_json matcher internally uses
JSON.parse
to decode JSON strings.
The default parser options used in the be_json matcher is { symbolize_names: true },
so you need to pass a hash with symbol keys as an expected structure.
If you prefer string keys, add the following snippet to your spec/spec_helper.rb:
# spec/spec_helper.rb
RSpec::ComposableJSONMatchers.configure do |config|
config.parser_options = { symbolize_names: false }
endLicense
Copyright (c) 2016 Yuji Nakayama
See the LICENSE.txt for details.