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 install
Enabling 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
end
If 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
end
Usage
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 names
matching
- Alias of
match
matcher - 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
include
matcher - 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
-
all
matcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json all be_a(String)
containing_exactly
- Alias of
contain_exactly
matcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json containing_exactly('bar', 'foo')
starting_with
- Alias of
start_with
matcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json starting_with('foo')
ending_with
- Alias of
end_with
matcher - Supported structure: Array
- Accepts matchers as arguments: Yes
expect('["foo", "bar"]').to be_json ending_with('bar')
having_attributes
- Alias of
have_attributes
matcher - 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_of
matcher - 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 }
end
License
Copyright (c) 2016 Yuji Nakayama
See the LICENSE.txt for details.