Read more

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

Dominik Schöler
March 16, 2016Software engineer at makandra GmbH

RSpec provides a nice diff when certain matchers fail.

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

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.

Posted by Dominik Schöler to makandra dev (2016-03-16 07:54)