structure_compare
Compares the structure of two deep nested Ruby structures
General
Use case: you're writing an API response or a JSON export and want to unit test it. Optionally you can ignore the leaf values or any hash key types/order (see below).
Gives error results with the path to where exactly the structures differ.
This README file is mirrored on my blog
Installation
gem install structure_compare
or add it to your Gemfile
:
gem structure_compare
quick-n-dirty example:
require 'structure_compare'
comparison = StructureCompare::StructureComparison.new
expected = { a: 1, b: 2, c: [1, 2, 3] }
actual = { a: 1, b: 2, c: [1, 2, "A"] }
comparison.structures_are_equal?(expected, actual)
puts comparison.error
# => root[:c][2] : expected String to be kind of Fixnum
MiniTest
require 'structure_compare'
require 'structure_compare/minitest'
assert_structures_equal({ a: 1, b: 2 }, { a: 1, b: 2 })
refute_structures_equal({ a: 1, b: 2 }, { c: 1, d: 2 })
Options
Strict key ordering
name: strict_key_order
(default: false
)
expected = { a: 1, b: 2 }
actual = { b: 2, a: 1 }
comparison = StructureCompare::StructureComparison.new(strict_key_order: false)
comparison.structures_are_equal?(expected, actual)
# => true
Value checking
name: check_values
(default: true
)
expected = { a: 1, b: { c: 1 } }
actual = { a: 8, b: { c: 8 } }
comparison = StructureCompare::StructureComparison.new
comparison.structures_are_equal?(expected, actual)
# => false
comparison = StructureCompare::StructureComparison.new(check_values: false)
comparison.structures_are_equal?(expected, actual)
# => true
Indifferent Access
Hash symbol keys are treated as equal to string keys
NOTE: an exception will be raised if there's a key present as symbol and string
name: indifferent_access
(default: false
)
expected = { a: 1 }
actual = { "a" => 1 }
comparison = StructureCompare::StructureComparison.new
comparison.structures_are_equal?(expected, actual)
# => false
comparison = StructureCompare::StructureComparison.new(indifferent_access: true)
comparison.structures_are_equal?(expected, actual)
# => true
hash = { a: 1, "a" => 2 }
comparison = StructureCompare::StructureComparison.new(indifferent_access: true)
comparison.structures_are_equal?(hash, hash)
# => StructureCompare::IndifferentAccessError
Float tolerance
When dealing with floats, you will want to introduce a tolerance.
NOTE: Float::EPSILON is always used for comparing Float type values.
NOTE: The check_values
option must be set.
name: float_tolerance_factor
(default: 0
)
tolerance = +- (expected * (1.0 + tolerance_factor) + Float::EPSILON)
This means a float_tolerance_factor
setting of 0.01 means that actual
can be 1% different from expected
to still be treated equal.
expected = { a: 10.0 }
actual_1 = { a: 10.1 }
actual_2 = { a: 10.11 }
# 1% tolerance factor
comparison = StructureCompare::StructureComparison.new(
float_tolerance_factor: 0.01, check_values: true
)
comparison.structures_are_equal?(expected, actual_1)
# => true
comparison.structures_are_equal?(expected, actual_2)
# => false
TODOS
RSpec helpers. Refactoring.
Contributing
Fork me and send me a pull request with your feature and working tests, or just request a feature.
License
MIT License, see LICENSE
file in the root directory