wikichen
wikichen

Reputation: 2253

Ruby on Rails tutorial: RSpec test failing when refactoring with matcher

I'm working through Michael Hartl's Rails tutorial and can't get one Rspec test to pass when refactoring with a matcher.

terminal output

Failures:

  1) Authentication login with invalid information
     Failure/Error: it { should have_error_message('Invalid') }
     NoMethodError:
       undefined method `has_error_message?' for #<Capybara::Session>
     # ./spec/requests/authentication_pages_spec.rb:21:in `block (4 levels) in <top (required)>'

spec/support/utilities.rb

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

spec/requests/authentication_pages_spec.rb

require 'spec_helper'

describe "Authentication" do 

  subject { page }

  describe "login page" do
    before { visit login_path }

    it { should have_selector('h1',    text: 'Login') }
    it { should have_selector('title', text: 'Login') }
  end

  describe "login" do
    before { visit login_path }

    describe "with invalid information" do
      before { click_button "Login" }

      it { should have_selector('title', text: 'Login') }
      it { should have_error_message('Invalid') }
    end

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

    describe "with valid information" do
      let(:user) { FactoryGirl.create(:user) }
      before { valid_login(user) }

      it { should have_selector('title', text: user.name) }
      it { should have_link('Profile', href: user_path(user)) }
      it { should have_link('Logout', href: logout_path) }
      it { should_not have_link('Login', href: login_path) }

      describe "followed by logout" do
        before { click_link "Logout" }

        it { should have_link('Login') }
      end
    end
  end
end

Why is it complaining about a has_error_message? method that's not defined anywhere?

Edited/added:

spec/spec_helper.rb

require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
#require 'spork/ext/ruby-debug'

Spork.prefork do
  # Loading more in this block will cause your tests to run faster. However,
  # if you change any configuration or code from libraries loaded here, you'll
  # need to restart spork for it take effect.
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  # Requires supporting ruby files with custom matchers and macros, etc,
  # in spec/support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  RSpec.configure do |config|
    # ## Mock Framework
    #
    # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
    #
    # config.mock_with :mocha
    # config.mock_with :flexmock
    # config.mock_with :rr
    config.mock_with :rspec

    # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
    config.fixture_path = "#{::Rails.root}/spec/fixtures"

    # If you're not using ActiveRecord, or you'd prefer not to run each of your
    # examples within a transaction, remove the following line or assign false
    # instead of true.
    config.use_transactional_fixtures = true

    # If true, the base class of anonymous controllers will be inferred
    # automatically. This will be the default behavior in future versions of
    # rspec-rails.
    config.infer_base_class_for_anonymous_controllers = false
  end
end

Spork.each_run do
  # This code will be run each time you run your specs.

end

Upvotes: 3

Views: 837

Answers (2)

Paul Fioravanti
Paul Fioravanti

Reputation: 16793

If you're using a later version of Capybara or RSpec (ie not the ones explicitly specified in the tutorial), if I recall correctly the way that those custom matchers needed to be written changed (though I could be wrong about that) Nevertheless, try changing your method from

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

to:

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

This will then hopefully enable you to write tests like:

it { should have_error_message("Invalid") }
it { should_not have_error_message("Invalid") }

I had a similar issue that I outlined in this StackOverflow Q&A.

Upvotes: 2

Stuart M
Stuart M

Reputation: 11588

Are you require'ing your spec/support/utilities.rb file? You may need something like this in your spec/spec_helper.rb file to actually load the file where you define the custom matcher:

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

Update: If you're using Spork, your utilities.rb file may be getting cached by the Spork server, meaning you would need to restart your master Spork process before the new matcher could be used by RSpec (see details on the Spork wiki).

Some people use this trick to force Spork to always reload support files:

Spork.each_run do
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
end

Upvotes: 3

Related Questions