SuperDiff is a gem that hooks into RSpec to intelligently display the differences between two data structures of any type.
📢 See what's changed in the latest version (0.2.0).
The primary motivation behind this gem is to vastly improve upon RSpec's built-in diffing capabilities.
Sometimes, whenever you use a matcher such as eq
, match
, include
, or
have_attributes
, you will get a diff of the two data structures you are trying
to match against. This is great if all you want to do is compare multi-line
strings. But if you want to compare other, more "real world" kinds of values —
nested data structures (arrays, hashes, and full-scale objects), such as what
you might work with when developing API endpoints or testing methods that make
database calls and return a set of model objects — then you are out of luck.
Since RSpec merely runs your expected
and actual
values through Ruby's
PrettyPrinter library and then performs a diff of these
strings, the output it produces leaves much to be desired.
For instance, let's say you wanted to compare these two hashes:
actual = {
customer: {
person: SuperDiff::Test::Person.new(name: "Marty McFly, Jr.", age: 17),
shipping_address: {
line_1: "456 Ponderosa Ct.",
city: "Hill Valley",
state: "CA",
zip: "90382"
}
},
items: [
{
name: "Fender Stratocaster",
cost: 100_000,
options: ["red", "blue", "green"]
},
{ name: "Mattel Hoverboard" }
]
}
expected = {
customer: {
person: SuperDiff::Test::Person.new(name: "Marty McFly", age: 17),
shipping_address: {
line_1: "123 Main St.",
city: "Hill Valley",
state: "CA",
zip: "90382"
}
},
items: [
{
name: "Fender Stratocaster",
cost: 100_000,
options: ["red", "blue", "green"]
},
{ name: "Chevy 4x4" }
]
}
If, somewhere in a test, you were to say:
expect(actual).to eq(expected)
You would get output that looks like this:
What this library does is to provide a diff engine that knows how to figure out the differences between any two data structures and display them in a sensible way. So, using the example above, you'd get this instead:
Want to try out this gem for yourself? As with most development-related gems, there are a couple ways depending on your type of project:
If you're developing a Rails app, add the following to your Gemfile:
gem "super_diff"
After running bundle install
, add the following to your rails_helper
:
require "super_diff/rspec-rails"
You're done!
If you're developing a library, add the following to your gemspec:
spec.add_development_dependency "super_diff"
Now add the following to your spec_helper
:
require "super_diff/rspec"
You're done!
As capable as this library is, it doesn't know how to deal with every kind of
object out there. You might find it necessary to instruct the gem on how to diff
your object. To do this, you can use a configuration block. Simply add this to
your test helper file (rails_helper
or spec_helper
):
SuperDiff::RSpec.configure do |config|
config.add_extra_differ_class(YourDiffer)
config.add_extra_operational_sequencer_class(YourOperationalSequencer)
config.add_extra_diff_formatter_class(YourDiffFormatter)
end
(More info here in the future on adding a custom differ, operational sequencer, and diff formatter. Also explanations on what these are.)
If you encounter a bug or have an idea for how this could be better, feel free to create an issue.
If you'd like to submit a PR instead, here's how to get started. First, fork this repo. Then, when you've cloned your fork, run:
bin/setup
This will install various dependencies. After this, you can run all of the tests:
bundle exec rake
If you update one of the tests, you can run it like so:
bin/rspec spec/integration/...
bin/rspec spec/unit/...
Finally, submit your PR and I'll take a look at it when I get a chance.
super_diff
is tested to work with Ruby >= 2.4.x, RSpec 3.x, and
Rails >= 5.x.
In developing this gem I made use of or was heavily inspired by these libraries:
- Diff::LCS, the library I started with in the original version of this gem (made in 2011!)
- The pretty-printing algorithms and API within PrettyPrinter and AwesomePrint, from which I borrowed ideas to develop the inspectors.
Thank you so much!
© 2018-2019 Elliot Winkler, released under the MIT license.