Failing RSpec have_attributes with arguments matchers

Why when using have_attributes with multiple argument matchers and only one is a failure, RSpecs fails all the rest of matchers ?

Published · 154 words · View as Markdown


Why when using have_attributes with multiple argument matchers and only one is a failure, RSpec fails all the rest of matchers ?

Code example#

Ruby
require 'rspec'

Customer = Struct.new(:id, :name)

RSpec.describe 'fail' do
  subject { Customer.new(123, 'Some Name') }

  it 'fails' do
    expect(subject).to have_attributes(
      id: kind_of(String),
      name: kind_of(String)
    )
  end
end

Failure#

Failing spec with have_attributes & kind_of

Although only the id is invalid we see both :name & :id as failed.

Why?#

When it’s time for RSpec to output the failure results it diffs the expected values with the given. In order to diff object rspecs uses RSpec::Support::Differ that turns them into strings by inspecting them.

Since we use the RSpec::Mocks::ArgumentMatchers::KindOf argument matcher RSpec matches it with the
RSpec::Support::ObjectFormatter::DescribableMatcherInspector and then we get the kind of String.

You can replicate it by running:

Ruby
RSpec::Support::ObjectFormatter::DescribableMatcherInspector.new(
  RSpec::Mocks::ArgumentMatchers::KindOf.new(String)
).inspect

When it’s time to compare the diffs we compare the kind of String with Some Name and we end up with the red error output although the matcher did not fail for that particular attribute.

Here is a Github issue for that.