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 UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
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)