alexbhandari
alexbhandari

Reputation: 1398

RSpec Test Using Matchers is Failing with a Non-descriptive Error

My problem with an RSpec test: I'm following Michael Hartl's Rails tutorial. We define an RSpec matcher have_error_message that is working for me. I am trying to define a matcher to test the inverse called not_have_error_message, which I invoke

it { should not_have_error_message }.

I am doing this because I tried it { should_not have_error_message } and got the error

Capybara::ExpectationNotMet: expected to find css "div.alert.alert-error" with text "Invalid" but there were no matches

I would like to know 1) if it is possible to accomplish this using should_not and have_error_message and 2) what I am doing wrong that is causing not_have_error_message to fail. Thanks!

Here's the code:

spec/features/authentication_pages_spec.rb (irrelevant part omitted)

describe "Authentication" do
  
  subject { page }

  describe "signin" do

    ...

    describe "with invalid information" do
      before { click_button "Sign in" }

      it { should have_content('Sign in') }
      it { should have_title('Sign in') }
      it { should have_error_message('Invalid') } 

      describe "after visiting another page" do
         before { click_link "Home" }
         it { should_not have_selector('div.alert.alert-error') }
         it { should not_have_error_message }
      end
    end
  end
end

spec/support/utilities.rb

include ApplicationHelper

def valid_signin(user)
   fill_in "Email",     with: user.email
   fill_in "Password",  with: user.password
   click_button "Sign in"
end

RSpec::Matchers.define :have_error_message do |message|
   match do |page|
      expect(page).to have_selector('div.alert.alert-error', text: message)
   end
end

RSpec::Matchers.define :not_have_error_message do
   match do |page|
      expect(page).not_to have_selector('div.alert.alert-error')
   end
end

And I'm getting the error:

  1) Authentication signin with invalid information after visiting another page 
     Failure/Error: it { should not_have_error_message }
       expected #<Capybara::Session> to not have error message
     # ./spec/features/authentication_pages_spec.rb:44:in `block (5 levels) in <top (required)>'

You can see I also run the equivalent test:

it { should_not have_selector('div.alert.alert-error') }

directly above the failing test and it passes.

Upvotes: 0

Views: 728

Answers (1)

Justin Ko
Justin Ko

Reputation: 46846

When defining the RSpec matchers, the block is meant to return true or false (see this wiki).

The way you have defined your matchers, you are actually making the assertion within an assertion. When the inner expectation fails, this raises an exception that causes the outer expectation to give unexpected results.

So that the custom matchers evaluate to true/false, use the has_selector? and has_not_selector? methods. Your custom matchers should be:

RSpec::Matchers.define :have_error_message do |message|
   match do |page|
      page.has_selector?('div.alert.alert-error', text: message)
   end
end

RSpec::Matchers.define :not_have_error_message do
   match do |page|
      page.has_no_selector?('div.alert.alert-error')
   end
end

Upvotes: 1

Related Questions