Heads up: RSpec's diffs may not tell the truth

RSpec provides a nice diff when certain matchers fail.

Here is an example where this diff is helpful while comparing two hashes:

{a:1}.should match(a:1, b:2)

Failure/Error: {a:1}.should match(a:1, b:2)
  expected {:a=>1} to match {:a=>1, :b=>2}
  Diff:
  @@ -1,3 +1,2 @@
   :a => 1,
  -:b => 2,

Unfortunately, this diff is not as clever as it would need to. RSpec's instance_of matchers will look like errors in the diff (even if they are not), and time objects that differ only in milliseconds won't appear in the diff (although they should):

# Because they are evaluated after each other, the two timestamps below differ marginally
{t: Time.now, h: Hash.new}.should match(t: Time.now, h: instance_of(Hash))
     
Failure/Error: {t: Time.now, h: Hash.new}.should match(t:Time.now, h: instance_of(Hash))
  expected {:t=>2016-03-16 08:02:16 +0100, :h=>{}} to match {:t=>2016-03-16 08:02:16 +0100, :h=>#<RSpec::Mocks::ArgumentMatchers::InstanceOf:0x0000000b87ce58 @klass=Hash>}
  Diff:
  @@ -1,3 +1,3 @@
  -:h => #<RSpec::Mocks::ArgumentMatchers::InstanceOf:0x0000000b87ce58 @klass=Hash>,
  +:h => {}, # <== Looks like an error, but is not: {} matches the matcher above
   :t => 2016-03-16 08:02:16 +0100, # <== Actual error: milliseconds differ

Note that while the {} vs instance_of(Hash) oddity shows up in the diff, it is not treated as an error. As soon as the millisecond offset is fixed (see this card for details), the spec will pass.

Keep this in mind and don't trust RSpec's diffs too much.

Dominik Schöler About 8 years ago