Search code examples
ruby-on-railsrspecrspec2rspec-rails

RSpec Custom Redirect Matcher Always Successful


I have followed an example in the book Everyday Rails Testing with RSpec to create a custom RSpec matcher for redirecting to the login page (requires_login matcher). The code to this matcher can be seen below:

RSpec::Matchers.define :require_login do

  match do |actual|
    redirect_to Rails.application.routes.url_helpers.login_path
  end

  failure_message_for_should do |actual|
    "expected to require login to access the method"
  end

  failure_message_for_should_not do |actual|
    "expected not to require login to access method"
  end

  description do
    "redirect to the login form"
  end

end

This matcher is then called in the example below:

describe "GET #edit" do

  it "requires login" do
    user = FactoryGirl.create(:user)
    get :edit, id: user
    expect(response).to require_login
  end

end # End GET #edit

For some reason the above test validates while the following does not (even though it too checks for a redirect to the login path):

describe "GET #edit" do

  it "requires login" do
    user = FactoryGirl.create(:user)
    get :edit, id: user
    expect(response).to redirect_to login_path
  end

end # End GET #edit

I have looked over my code numerous times as well as the example in the book mentioned above and can't find any errors. Any insight into how to correctly achieve this matcher would be greatly appreciated. Also I am using Ruby 2.0.0, Rails 3.2.13, and RSpec-Rails 2.13.1.

Solution (thanks to Frederick Cheung's advice)

RSpec::Matchers.define :require_login do |attribute|

  match do |actual|
    expect(attribute).to redirect_to Rails.application.routes.url_helpers.login_path
  end

  failure_message_for_should do |actual|
    "expected to require login to access the method"
  end

  failure_message_for_should_not do |actual|
    "expected not to require login to access method"
  end

  description do
    "redirect to the login form"
  end

end

Solution

  • Your match block needs to return whether the matched matched, but at the moment you are returning something that is always truthy.

    You probably want to take a similar approach to the redirect_to matcher which calls the rails provided assert_redirected_to and returns true/false depending on whether ActiveSupport::TestCase::Assertion is raised or not