spec/unit/support/matchers/match_json_response_spec.rb
require 'spec_helper'
RSpec.describe 'match_json_response matcher' do
it 'passes when two hashes are identical' do
expect({ 'a' => 1 }).to match_json_response({ a: 1 })
end
it 'passes with array values' do
expect({ 'a' => [1, 2] }).to match_json_response({ a: [1, 2] })
end
context 'with regular expression matching' do
it 'passes when comparing a string' do
expect({ 'a' => '1' }).to match_json_response({ a: /\d+/ })
end
it 'fails when comparing an integer' do
expect do
expect({ 'a' => 1 }).to match_json_response({ a: /\d+/ })
end.to raise_expectation_not_met_with_summary_parts('! a:', '- /\d+/', '+ 1')
end
it 'passes when matching an iso8601 timestamp' do
expect({ 'a' => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ') }).to match_json_response({ a: iso8601 })
end
it 'does not interpret strings as regex' do
expect do
expect({ 'a' => '[thing]' }).to match_json_response({ a: '[th]' })
end.to raise_expectation_not_met_with_summary_parts('! a:', '- [th]', '+ [thing]')
end
end
it 'fails when values do not match in a top-level key' do
expect do
expect({ 'a' => 1 }).to match_json_response({ a: 2 })
end.to raise_expectation_not_met_with_summary_parts('! a:', '- 2', '+ 1')
end
it 'fails when values do not match in a nested key' do
expect do
expect({ 'a' => { 'b' => 1 } }).to match_json_response({ a: { b: 2 } })
end.to raise_expectation_not_met_with_summary_parts('! a.b:', '- 2', '+ 1')
end
it 'fails when expected contains a key missing in actual' do
expect do
expect({}).to match_json_response({ a: 1 })
end.to raise_expectation_not_met_with_summary(/- a: 1/)
end
it 'fails when actual contains a key missing in expected' do
expect do
expect({ 'a' => 1 }).to match_json_response({})
end.to raise_expectation_not_met_with_summary(/\+ a: 1/)
end
it 'fails when array values do not match' do
expect do
expect({ 'a' => [1, 2] }).to match_json_response({ a: [1] })
end.to raise_expectation_not_met_with_summary(/\+ a\[1\]: 2/)
end
it 'fails when array values are permuted' do
expect do
expect({ 'a' => [1, 2] }).to match_json_response({ a: [2, 1] })
end.to raise_expectation_not_met_with_summary(/\+ a\[0\]: 1\n\s+- a\[2\]: 1/)
end
it 'fails when hash values within arrays do not match' do
expect do
expect({ 'a' => [{ 'b' => 1 }] }).to match_json_response({ a: [{ b: 2 }] })
end.to raise_expectation_not_met_with_key_change(expected: '- a[0]: {:b=>2}', actual: '+ a[0]: {:b=>1}')
end
it 'fails when hash keys within arrays do not match' do
expect do
expect({ 'a' => [{ 'b' => 1 }] }).to match_json_response({ a: [{ c: 1 }] })
end.to raise_expectation_not_met_with_key_change(expected: '- a[0]: {:c=>1}', actual: '+ a[0]: {:b=>1}')
end
it 'fails when actual is empty but expected is an empty array' do
expect do
expect({}).to match_json_response({ a: [] })
end.to raise_expectation_not_met_with_summary(/- a: \[\]/)
end
it 'fails when actual as a non-empty but expected is an empty array' do
expect do
expect({ 'a' => [{ 'b' => 1 }] }).to match_json_response({ a: [] })
end.to raise_expectation_not_met_with_summary(/\+ a\[0\]: \{:b=>1\}/)
end
it 'fails on deeply nested value mismatches' do
expect do
expect({ 'a' => [{ 'a' => { 'a' => [{ 'a' => 1 }, { 'b' => 2 }] } }] }).to match_json_response({ a: [{ a: { a: [{ a: 1 }, { b: 1 }] } }] })
end.to raise_expectation_not_met_with_key_change(expected: '- a[0].a.a[1]: {:b=>1}',
actual: '+ a[0].a.a[1]: {:b=>2}')
end
it 'outputs submatcher error message when it errors' do
expect do
expect({ 'a' => %w[a b] }).to match_json_response({ a: contain_exactly('a', 'j') })
end.to raise_expectation_not_met_with_summary_parts(
'expected collection contained: ["a", "j"]',
'actual collection contained: ["a", "b"]',
'the missing elements were: ["j"]',
'the extra elements were: ["b"]'
)
end
it 'does not display the submatchers when another field errors' do
expect do
expect({ 'a' => %w[a b], 'b' => 1 }).to match_json_response({ a: contain_exactly('a', 'b'), b: 2 })
end.to raise_error(RSpec::Expectations::ExpectationNotMetError) { |error| expect(error.message).not_to match(/!\s*a:/) }
end
def raise_expectation_not_met_with_summary(ptn)
raise_error(RSpec::Expectations::ExpectationNotMetError, ptn)
end
def raise_expectation_not_met_with_key_change(expected:, actual:)
ptn = Regexp.new(Regexp.escape(expected) + '\n\s+' + Regexp.escape(actual))
raise_error(RSpec::Expectations::ExpectationNotMetError, ptn)
end
def raise_expectation_not_met_with_summary_parts(*parts)
ptn = Regexp.new(parts.map { |part| Regexp.escape(part) }.join('\n\s*'))
raise_error(RSpec::Expectations::ExpectationNotMetError, ptn)
end
end