Skip to content

Instantly share code, notes, and snippets.

@bakineggs
Created June 6, 2012 19:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bakineggs/2883923 to your computer and use it in GitHub Desktop.
Save bakineggs/2883923 to your computer and use it in GitHub Desktop.
matchers for testing that one hash/array is contained in another
# contain_array checks to see if one array contains all of the elements from another array in the same relative order
#
# Any elements that are hashes are matched using contain_hash
# Any elements that are arrays are matched using contain_array
#
# Examples:
#
# describe 'something' do
# subject do
# [:a, {:b => [:c, :d, :e]}]
# end
#
# it { should contain_array [:a] } # pass
# it { should contain_array [:b] } # fail
# it { should contain_array [{:b => [:c]}] } # pass
# it { should contain_array [{:b => [:f]}] } # fail
# it { should contain_array [{:b => [:c, :e]}] } # pass
# it { should contain_array [{:b => [:e, :c]}] } # fail
# it { should contain_array } # pass
# end
require 'contains_hash'
class ContainsArray
def initialize expected_contents
@expected_contents = expected_contents
end
def matches? actual_array
@actual_array = actual_array
return false unless @expected_contents.is_a?(Array) && actual_array.is_a?(Array)
return false if @expected_contents.length > actual_array.length
index = 0
@expected_contents.each do |value|
index += 1 while index < actual_array.length && !element_matches?(value, actual_array[index])
return false if index == actual_array.length
end
true
end
def failure_message_for_should
"Expected to contain array, but did not.\n#{common_failure_message}"
end
def failure_message_for_should_not
"Expected not to contain array, but did.\n#{common_failure_message}"
end
def description
"contain array #{@expected_contents.inspect}"
end
private
def element_matches? expected, actual
if expected.is_a? Hash
ContainsHash.new(expected).matches? actual
elsif expected.is_a? Array
ContainsArray.new(expected).matches? actual
else
expected == actual
end
end
def common_failure_message
"Actual Array: #{@actual_array.inspect}\nExpected Contents: #{@expected_contents.inspect}"
end
end
def contain_array expected_contents = []
ContainsArray.new expected_contents
end
require 'contains_array'
describe ContainsArray do
let(:matcher) { ContainsArray.new expected_contents }
describe '#matches?' do
let(:actual_array) { [:a, {:b => [:c, :d, :e], :f => :g}] }
subject { matcher.matches? actual_array }
context 'when the expected contents are empty' do
let(:expected_contents) { [] }
it { should be_true }
end
context 'when all elements are in the array' do
let(:expected_contents) { [:a] }
it { should be_true }
end
context 'when no elements are in the array' do
let(:expected_contents) { [:b] }
it { should be_false }
end
context 'when not all elements are in the array' do
let(:expected_contents) { [:a, :b] }
it { should be_false }
end
context 'when a hash element partially matches' do
let(:expected_contents) { [{:f => :g}] }
it { should be_true }
end
context 'when elements match in order' do
let(:expected_contents) { [:a, {:f => :g}] }
it { should be_true }
end
context 'when elements match out of order' do
let(:expected_contents) { [{:f => :g}, :a] }
it { should be_false }
end
context 'when a hash element contains an array that partially matches in order' do
let(:expected_contents) { [{:b => [:d, :e]}] }
it { should be_true }
end
context 'when a hash element contains an array that partially matches out of order' do
let(:expected_contents) { [{:b => [:e, :d]}] }
it { should be_false }
end
end
end
# contain_hash checks to see if one hash contains all of the keys and values from another hash
#
# Any values that are hashes are matched using contain_hash
# Any values that are arrays are matched using contain_array
#
# Examples:
#
# describe 'something' do
# subject do
# {:a => :b, :c => [:d, :e, :f]}
# end
#
# it { should contain_hash(:a => :b) } # pass
# it { should contain_hash(:a => :c) } # fails
# it { should contain_hash(:a => :b, :c => [:e]) } # pass
# it { should contain_hash(:a => :b, :c => [:g]) } # fail
# it { should contain_hash(:a => :b, :c => [:d, :f]) } # pass
# it { should contain_hash(:a => :b, :c => [:f, :d]) } # fail
# it { should contain_hash } # pass
# end
require 'contains_array'
class ContainsHash
def initialize expected_contents
@expected_contents = expected_contents
end
def matches? actual_hash
@actual_hash = actual_hash
return false unless @expected_contents.is_a?(Hash) && actual_hash.is_a?(Hash)
return false unless (@expected_contents.keys - actual_hash.keys).empty?
@expected_contents.each do |key, value|
actual_value = actual_hash[key]
if value.is_a? Hash
return false unless ContainsHash.new(value).matches? actual_value
elsif value.is_a? Array
return false unless ContainsArray.new(value).matches? actual_value
else
return false unless value == actual_value
end
end
true
end
def failure_message_for_should
"Expected to contain hash, but did not.\n#{common_failure_message}"
end
def failure_message_for_should_not
"Expected not to contain hash, but did.\n#{common_failure_message}"
end
def description
"contain hash #{@expected_contents.inspect}"
end
private
def common_failure_message
"Actual Hash: #{@actual_hash.inspect}\nExpected Contents: #{@expected_contents.inspect}"
end
end
def contain_hash expected_contents = {}
ContainsHash.new expected_contents
end
require 'contains_hash'
describe ContainsHash do
let(:matcher) { ContainsHash.new expected_contents }
describe '#matches?' do
let(:actual_hash) { {:a => :b, :c => [:d, :e, :f, {:g => :h, :i => :j}]} }
subject { matcher.matches? actual_hash }
context 'when the expected contents are empty' do
let(:expected_contents) { {} }
it { should be_true }
end
context 'when all keys and values are in the hash' do
let(:expected_contents) { {:a => :b} }
it { should be_true }
end
context 'when a key has a different value in the hash' do
let(:expected_contents) { {:a => :c} }
it { should be_false }
end
context 'when a key with an array value has a match in order' do
let(:expected_contents) { {:a => :b, :c => [:d, :f]} }
it { should be_true }
end
context 'when a key with an array value has a match out of order' do
let(:expected_contents) { {:a => :b, :c => [:f, :d]} }
it { should be_false }
end
context 'when a key with an array value contains a hash that partially matches' do
let(:expected_contents) { {:a => :b, :c => [{:g => :h}]} }
it { should be_true }
end
context 'when a key with an array value contains a hash that does not match' do
let(:expected_contents) { {:a => :b, :c => [{:g => :k}]} }
it { should be_false }
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment