module Pact::Matchers
Should be called Differs Note to self: Some people are using this module directly, so if you refactor it maintain backwards compatibility
Constants
- DEFAULT_OPTIONS
- NO_DIFF
- NO_DIFF_AT_INDEX
- NUMERIC_TYPES
Public Instance Methods
diff(expected, actual, opts = {})
click to toggle source
# File lib/pact/matchers/matchers.rb, line 34 def diff expected, actual, opts = {} calculate_diff(expected, actual, DEFAULT_OPTIONS.merge(configurable_options).merge(opts)) end
type_diff(expected, actual, opts = {})
click to toggle source
# File lib/pact/matchers/matchers.rb, line 38 def type_diff expected, actual, opts = {} calculate_diff expected, actual, DEFAULT_OPTIONS.merge(configurable_options).merge(opts).merge(type: true) end
Also aliased as: structure_diff
Private Instance Methods
actual_array_diff(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 103 def actual_array_diff expected, actual, options difference = [] diff_found = false length = [expected.length, actual.length].max length.times do | index| expected_item = expected.fetch(index, Pact::UnexpectedIndex.new) actual_item = actual.fetch(index, Pact::IndexNotFound.new) if (item_diff = calculate_diff(expected_item, actual_item, options)).any? diff_found = true difference << item_diff else difference << NO_DIFF_AT_INDEX end end diff_found ? difference : NO_DIFF end
actual_hash_diff(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 142 def actual_hash_diff expected, actual, options hash_diff = expected.each_with_object({}) do |(key, expected_value), difference| diff_at_key = calculate_diff_at_key(key, expected_value, actual, difference, options) difference[key] = diff_at_key if diff_at_key.any? end hash_diff.merge(check_for_unexpected_keys(expected, actual, options)) end
actual_regexp_diff(regexp, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 87 def actual_regexp_diff regexp, actual, options if regexp.match(actual) NO_DIFF else RegexpDifference.new regexp, actual, "Expected a String matching #{regexp.inspect} but got #{short_description(actual)} at <path>" end end
actual_term_diff(term, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 71 def actual_term_diff term, actual, options if term.matcher.match(actual) NO_DIFF else RegexpDifference.new term.matcher, actual, "Expected a String matching #{term.matcher.inspect} (like #{term.generate.inspect}) but got #{actual.inspect} at <path>" end end
array_diff(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 95 def array_diff expected, actual, options if actual.is_a? Array actual_array_diff expected, actual, options else Difference.new Pact::Reification.from_term(expected), actual, type_difference_message(Pact::Reification.from_term(expected), actual) end end
array_like_diff(array_like, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 120 def array_like_diff array_like, actual, options if actual.is_a? Array expected_size = [array_like.min, actual.size].max # I know changing this is going to break something, but I don't know what it is, as there's no # test that fails when I make this change. I know the unpack regexps was there for a reason however. # Guess we'll have to change it and see! # expected_array = expected_size.times.collect{ Pact::Term.unpack_regexps(array_like.contents) } expected_array = expected_size.times.collect{ array_like.contents } actual_array_diff expected_array, actual, options.merge(:type => true) else Difference.new array_like.generate, actual, type_difference_message(array_like.generate, actual) end end
calculate_diff(expected, actual, opts = {})
click to toggle source
# File lib/pact/matchers/matchers.rb, line 48 def calculate_diff expected, actual, opts = {} options = DEFAULT_OPTIONS.merge(opts) case expected when Hash then hash_diff(expected, actual, options) when Array then array_diff(expected, actual, options) when Regexp then regexp_diff(expected, actual, options) when Pact::SomethingLike then calculate_diff(expected.contents, actual, options.merge(:type => true)) when Pact::ArrayLike then array_like_diff(expected, actual, options) when Pact::Term then term_diff(expected, actual, options) else object_diff(expected, actual, options) end end
calculate_diff_at_key(key, expected_value, actual, difference, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 150 def calculate_diff_at_key key, expected_value, actual, difference, options actual_value = actual.fetch(key, Pact::KeyNotFound.new) diff_at_key = calculate_diff(expected_value, actual_value, options) if actual_value.is_a?(Pact::KeyNotFound) diff_at_key.message = key_not_found_message(key, actual) end diff_at_key end
check_for_unexpected_keys(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 159 def check_for_unexpected_keys expected, actual, options if options[:allow_unexpected_keys] NO_DIFF else (actual.keys - expected.keys).each_with_object({}) do | key, running_diff | running_diff[key] = Difference.new(UnexpectedKey.new, actual[key], "Did not expect the key \"#{key}\" to exist at <parent_path>") end end end
class_description(object)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 275 def class_description object return "nil" if object.nil? clazz = object.class case clazz.name[0] when /[AEIOU]/ then "an #{clazz}" else "a #{clazz}" end end
class_name_with_value_in_brackets(object)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 252 def class_name_with_value_in_brackets object object_desc = has_children?(object) && object.inspect.length < 100 ? "" : " (#{object.inspect})" object_desc = if object.nil? "nil" else "#{class_description(object)}#{object_desc}" end end
configurable_options()
click to toggle source
# File lib/pact/matchers/matchers.rb, line 44 def configurable_options { treat_all_number_classes_as_equivalent: Pact.configuration.treat_all_number_classes_as_equivalent } end
exact_value_diff(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 177 def exact_value_diff expected, actual, options if expected == actual NO_DIFF else Difference.new expected, actual, value_difference_message(expected, actual, options) end end
has_children?(object)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 217 def has_children? object object.is_a?(Hash) || object.is_a?(Array) end
hash_diff(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 134 def hash_diff expected, actual, options if actual.is_a? Hash actual_hash_diff expected, actual, options else Difference.new Pact::Reification.from_term(expected), actual, type_difference_message(Pact::Reification.from_term(expected), actual) end end
is_boolean(object)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 213 def is_boolean object object == true || object == false end
is_number?(object)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 208 def is_number? object # deal with Fixnum and Integer without warnings by using string class names NUMERIC_TYPES.include?(object.class.to_s) end
key_not_found_message(key, actual)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 261 def key_not_found_message key, actual hint = actual.any? ? "(keys present are: #{actual.keys.join(", ")})" : "in empty Hash" "Could not find key \"#{key}\" #{hint} at <parent_path>" end
object_diff(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 169 def object_diff expected, actual, options if options[:type] type_difference expected, actual, options else exact_value_diff expected, actual, options end end
regexp_diff(regexp, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 79 def regexp_diff regexp, actual, options if actual.is_a?(String) actual_regexp_diff regexp, actual, options else RegexpDifference.new regexp, actual, "Expected a String matching #{regexp.inspect} but got #{class_name_with_value_in_brackets(actual)} at <path>" end end
short_description(object)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 266 def short_description object return "nil" if object.nil? case object when Hash then "a Hash" when Array then "an Array" else object.inspect end end
term_diff(term, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 63 def term_diff term, actual, options if actual.is_a?(String) actual_term_diff term, actual, options else RegexpDifference.new term.matcher, actual, "Expected a String matching #{term.matcher.inspect} (like #{term.generate.inspect}) but got #{class_name_with_value_in_brackets(actual)} at <path>" end end
type_diff_actual_display(actual)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 197 def type_diff_actual_display actual actual.is_a?(KeyNotFound) ? actual : ActualType.new(actual) end
type_diff_expected_display(expected)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 193 def type_diff_expected_display expected ExpectedType.new(expected) end
type_difference(expected, actual, options)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 185 def type_difference expected, actual, options if types_match? expected, actual, options NO_DIFF else TypeDifference.new type_diff_expected_display(expected), type_diff_actual_display(actual), type_difference_message(expected, actual) end end
type_difference_message(expected, actual)
click to toggle source
# File lib/pact/matchers/matchers.rb, line 235 def type_difference_message expected, actual case expected when Pact::UnexpectedIndex "Actual array is too long and should not contain #{short_description(actual)} at <path>" else case actual when Pact::IndexNotFound "Actual array is too short and should have contained #{short_description(expected)} at <path>" else expected_desc = class_name_with_value_in_brackets(expected) expected_desc.gsub!("(", "(like ") actual_desc = class_name_with_value_in_brackets(actual) "Expected #{expected_desc} but got #{actual_desc} at <path>" end end end
types_match?(expected, actual, options = {})
click to toggle source
Make options optional to support existing monkey patches
# File lib/pact/matchers/matchers.rb, line 202 def types_match? expected, actual, options = {} expected.class == actual.class || (is_boolean(expected) && is_boolean(actual)) || (options.fetch(:treat_all_number_classes_as_equivalent, false) && is_number?(expected) && is_number?(actual)) end
value_difference_message(expected, actual, options = {})
click to toggle source
# File lib/pact/matchers/matchers.rb, line 221 def value_difference_message expected, actual, options = {} case expected when Pact::UnexpectedIndex "Actual array is too long and should not contain #{short_description(actual)} at <path>" else case actual when Pact::IndexNotFound "Actual array is too short and should have contained #{short_description(expected)} at <path>" else "Expected #{short_description(expected)} but got #{short_description(actual)} at <path>" end end end