Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested matchers are displayed in have_attributes error diff when any attribute fails validation #1067

Closed
tmertens opened this issue Jul 16, 2018 · 1 comment

Comments

@tmertens
Copy link

Subject of the issue

When a matcher is nested within the have_attributes matcher, then the attribute for the nested matcher(s) will show up in the error diff as failed whenever any attribute in the parent matcher fails validation. This makes the actual failed condition more difficult to determine, especially when multiple nested matchers are used in a spec.

Your environment

  • Ruby version: 2.4.1p111
  • rspec-expectations version: 3.6.0, 3.7.0

Steps to reproduce

Run the following spec and observe the output for the failing test shows the nested matchers as failed when in fact only the role attribute has an error (see output in Actual Behavior below):

RSpec.describe 'have_attributes nested messages' do
  subject do
    double(
      child: double(name: 'Elsa'),
      name: 'Agnarr',
      role: 'King',
      things: ['Staff', 'Crown']
    )
  end

  it 'only prints failing attributes on failure' do
    expect(subject).to have_attributes(
      name: 'Agnarr',
      role: 'Queen', # Only this field actually fails validation
      child: have_attributes(name: 'Elsa'),
      things: match_array(['Staff', 'Crown'])
    )
  end

  it 'passes' do
    expect(subject).to have_attributes(
      name: 'Agnarr',
      role: 'King',
      child: have_attributes(name: 'Elsa'),
      things: match_array(['Staff', 'Crown'])
    )
  end
end

Expected behavior

Only the failed attributes should be displayed in the error diff. Nested have_attributes or other matchers should not be displayed if they pass validation.

The output for the example test should look something like:

Failures:

  1) have_attributes nested messages only prints failing attributes on failure
     Failure/Error:
       expect(subject).to have_attributes(
         name: 'Agnarr',
         role: 'Queen',
         child: have_attributes(name: 'Elsa'),
         things: match_array(['Staff', 'Crown'])
       )
     
       expected #<Double (anonymous)> to have attributes {:name => "Agnarr", :role => "Queen", :child => (have attributes {:name => "Elsa"}), :things => (contain exactly "Staff" and "Crown")} but had attributes {:name => "Agnarr", :role => "King", :child => #<Double (anonymous)>, :things => ["Staff", "Crown"]}
       Diff:
       
       @@ -1,5 +1,5 @@
        :child => (have attributes {:name => "Elsa"}),
        :name => "Agnarr",
       -:role => "Queen",
       +:role => "King",
        :things => (contain exactly "Staff" and "Crown"),
       
     # ./spec/have_attributes_spec.rb:13:in `block (2 levels) in <top (required)>'

Actual behavior

Non-failed attributes are displayed in the error diff even if they would pass validation.

The output of the test above looks like:

Failures:

  1) have_attributes nested messages only prints failing attributes on failure
     Failure/Error:
       expect(subject).to have_attributes(
         name: 'Agnarr',
         role: 'Queen',
         child: have_attributes(name: 'Elsa'),
         things: match_array(['Staff', 'Crown'])
       )
     
       expected #<Double (anonymous)> to have attributes {:name => "Agnarr", :role => "Queen", :child => (have attributes {:name => "Elsa"}), :things => (contain exactly "Staff" and "Crown")} but had attributes {:name => "Agnarr", :role => "King", :child => #<Double (anonymous)>, :things => ["Staff", "Crown"]}
       Diff:
       
       @@ -1,5 +1,5 @@
       -:child => (have attributes {:name => "Elsa"}),
       +:child => #<Double (anonymous)>,
        :name => "Agnarr",
       -:role => "Queen",
       -:things => (contain exactly "Staff" and "Crown"),
       +:role => "King",
       +:things => ["Staff", "Crown"],
       
     # ./spec/have_attributes_spec.rb:13:in `block (2 levels) in <top (required)>'
@tmertens tmertens changed the title Nested have_attributes matchers are displayed in error diff when any attribute fails validation Nested matchers are displayed in have_attributes error diff when any attribute fails validation Jul 16, 2018
@myronmarston
Copy link
Member

Thanks for reporting this. See #1064 for a complete explanation, but the TL;DR is that this is a known issue since RSpec uses a textual differ. Any time the textual representation of two objects is different, it gets highlighted as a difference in the output, even if the objects match.

@benoittgt has started prototyping a new differ that should eventually solve these problems.

Closing since there's nothing actionable for us to do at this time (outside of the work @benoittgt is already doing).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants