Last active
August 29, 2015 14:24
-
-
Save groyoh/7869593e40c63af885ec to your computer and use it in GitHub Desktop.
Better hash matcher?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'bundler/inline' | |
gemfile(true) do | |
source 'https://rubygems.org' | |
gem 'activesupport', require: 'active_support' | |
gem 'colorize' | |
end | |
require 'colorize' | |
require 'active_support/core_ext/object/deep_dup' | |
RSpec::Matchers.define :match_hash do |expected| | |
match do |actual| | |
actual == expected | |
end | |
failure_message do |actual| | |
"Non matching:\n#{match(actual, expected)}" | |
end | |
def key(k) | |
klass = k.class | |
if klass == Symbol | |
":#{k}" | |
elsif klass == String | |
"\"#{k}\"" | |
else | |
k | |
end | |
end | |
def desc(prefix, suffix, value) | |
"\t * #{prefix}#{suffix} # => #{value}" | |
end | |
def match(actual, expected, suffix = "") | |
message = "" | |
if actual.is_a?(Hash) && expected.is_a?(Hash) | |
expected.each do |k,v| | |
puts key(k) | |
next_suffix = "#{suffix}[#{key(k)}]" | |
message << match(actual[k], v, next_suffix) | |
end | |
actual.each do |k,v| | |
next if expected.has_key?(k) | |
actual_desc = desc("actual", "#{suffix}[#{key(k)}]", v).green | |
message << "- actual#{suffix} has extra key #{key(k)}\n#{actual_desc}\n" | |
end | |
expected.each do |k,v| | |
next if actual.has_key?(k) | |
expected_desc = desc("expected", "#{suffix}[#{key(k)}]", v).red | |
message << "- actual#{suffix} is missing key #{key(k)}\n#{expected_desc}\n" | |
end | |
elsif actual.is_a?(Array) && expected.is_a?(Array) | |
expected_size = expected.size | |
actual_size = actual.size | |
actual.size.times do |i| | |
next_suffix = "#{suffix}[#{i}]" | |
message << match(actual[i], expected[i], next_suffix) | |
end | |
if expected_size > actual_size | |
(actual_size...expected_size).each do |i| | |
expected_desc = desc("expect", "#{suffix}[#{i}]", expected[i]).red | |
message << "- actual#{suffix} is missing element at index #{i}\n#{expected_desc}\n" | |
end | |
elsif actual_size > expected_size | |
(expected_size...actual_size).each do |i| | |
actual_desc = desc("actual", "#{suffix}[#{i}]", actual[i]).green | |
message << "- actual#{suffix} has extra element at index #{i}\n#{actual_desc}\n" | |
end | |
end | |
message | |
elsif actual != expected | |
actual_desc = desc("actual", suffix, actual.inspect).green | |
expected_desc = "\t * expect#{suffix} # => #{expected.inspect}".red | |
message << "- actual#{suffix} != expected#{suffix}:\n#{actual_desc}\n#{expected_desc}\n" | |
end | |
message | |
end | |
end | |
H = { | |
meta: { | |
per_page: 1 | |
}, | |
data: [{ | |
id: "123", | |
type: "user", | |
attributes: { | |
friends_count: 2, | |
}, | |
relationships: { | |
friends: { | |
data:[ | |
{ id: "12", type: "user" }, | |
{ id: "13", type: "user" } | |
] | |
} | |
} | |
}] | |
} | |
h1 = H.deep_dup | |
h1.delete(:meta) | |
RSpec.describe h1 do | |
it { is_expected.to match_hash(H) } | |
end | |
h2 = H.deep_dup | |
h2[:meta][:next_page] = 2 | |
RSpec.describe h2 do | |
it { is_expected.to match_hash(H) } | |
end | |
h3 = H.deep_dup | |
h3[:data][0][:relationships][:friends][:data].pop | |
RSpec.describe h3 do | |
it { is_expected.to match_hash(H) } | |
end | |
h4 = H.deep_dup | |
h4[:data][0][:relationships][:friends][:data] << { id: "15", type: "user" } | |
RSpec.describe h4 do | |
it { is_expected.to match_hash(H) } | |
end | |
h5 = H.deep_dup | |
h5[:data][0][:attributes].delete(:friends_count) | |
RSpec.describe h5 do | |
it { is_expected.to match_hash(H) } | |
end | |
h6 = H.deep_dup | |
h6[:data][0][:attributes]["is_admin"] = true | |
RSpec.describe h6 do | |
it { is_expected.to match_hash(H) } | |
end | |
__END__ | |
Failures: | |
1) {:data=>[{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]} should match hash [:meta, {:per_page=>1}] and [:data, [{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]] | |
Failure/Error: it { is_expected.to match_hash(H) } | |
Non matching: | |
- actual[:meta] != expected[:meta]: | |
* actual[:meta] # => nil | |
* expect[:meta] # => {:per_page=>1} | |
- actual is missing key :meta | |
* expected[:meta] # => {:per_page=>1} | |
# ./better_hash_matcher.rb:109:in `block (2 levels) in <top (required)>' | |
2) {:meta=>{:per_page=>1, :next_page=>2}, :data=>[{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]} should match hash [:meta, {:per_page=>1}] and [:data, [{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]] | |
Failure/Error: it { is_expected.to match_hash(H) } | |
Non matching: | |
- actual[:meta] has extra key :next_page | |
* actual[:meta][:next_page] # => 2 | |
# ./better_hash_matcher.rb:115:in `block (2 levels) in <top (required)>' | |
3) {:meta=>{:per_page=>1}, :data=>[{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}]}}}]} should match hash [:meta, {:per_page=>1}] and [:data, [{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]] | |
Failure/Error: it { is_expected.to match_hash(H) } | |
Non matching: | |
- actual[:data][0][:relationships][:friends][:data] is missing element at index 1 | |
* expect[:data][0][:relationships][:friends][:data][1] # => {:id=>"13", :type=>"user"} | |
# ./better_hash_matcher.rb:121:in `block (2 levels) in <top (required)>' | |
4) {:meta=>{:per_page=>1}, :data=>[{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}, {:id=>"15", :type=>"user"}]}}}]} should match hash [:meta, {:per_page=>1}] and [:data, [{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]] | |
Failure/Error: it { is_expected.to match_hash(H) } | |
Non matching: | |
- actual[:data][0][:relationships][:friends][:data][2] != expected[:data][0][:relationships][:friends][:data][2]: | |
* actual[:data][0][:relationships][:friends][:data][2] # => {:id=>"15", :type=>"user"} | |
* expect[:data][0][:relationships][:friends][:data][2] # => nil | |
- actual[:data][0][:relationships][:friends][:data] has extra element at index 2 | |
* actual[:data][0][:relationships][:friends][:data][2] # => {:id=>"15", :type=>"user"} | |
# ./better_hash_matcher.rb:127:in `block (2 levels) in <top (required)>' | |
5) {:meta=>{:per_page=>1}, :data=>[{:id=>"123", :type=>"user", :attributes=>{}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]} should match hash [:meta, {:per_page=>1}] and [:data, [{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]] | |
Failure/Error: it { is_expected.to match_hash(H) } | |
Non matching: | |
- actual[:data][0][:attributes][:friends_count] != expected[:data][0][:attributes][:friends_count]: | |
* actual[:data][0][:attributes][:friends_count] # => nil | |
* expect[:data][0][:attributes][:friends_count] # => 2 | |
- actual[:data][0][:attributes] is missing key :friends_count | |
* expected[:data][0][:attributes][:friends_count] # => 2 | |
# ./better_hash_matcher.rb:133:in `block (2 levels) in <top (required)>' | |
6) {:meta=>{:per_page=>1}, :data=>[{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2, "is_admin"=>true}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]} should match hash [:meta, {:per_page=>1}] and [:data, [{:id=>"123", :type=>"user", :attributes=>{:friends_count=>2}, :relationships=>{:friends=>{:data=>[{:id=>"12", :type=>"user"}, {:id=>"13", :type=>"user"}]}}}]] | |
Failure/Error: it { is_expected.to match_hash(H) } | |
Non matching: | |
- actual[:data][0][:attributes] has extra key "is_admin" | |
* actual[:data][0][:attributes]["is_admin"] # => true | |
# ./better_hash_matcher.rb:139:in `block (2 levels) in <top (required)>' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment