How to test for a redirect with Rspec and Capybara

32,926

Solution 1

Capybara is not a rails-specific solution so it doesn't know anything about rails's rendering logic.

Capybara is meant specifically for Integration testing, which is essentially running tests from the viewpoint of an end-user interacting with a browser. In these tests, you should not be asserting templates because an end-user can't see that deep into your application. What you should instead be testing is that an action lands you on the correct path.

current_path.should == new_user_path
page.should have_selector('div#erro_div')

Solution 2

you can do it this way:

expect(current_path).to eql(new_app_user_registration_path)

Solution 3

Rspec 3:

The easiest way to test for the current path is with:

expect(page).to have_current_path('/login?status=invalid_token')

The have_current_path has an advantage over this approach:

expect(current_path).to eq('/login')

because you can include query params.

Solution 4

The error message @request must be an ActionDispatch::Request tells you that rspec-rails matcher redirect_to (it delegates to Rails assert_redirected_to) expects it to be used in Rails functional tests (should mix in ActionController::TestCase). The code you posted looks like rspec-rails request spec. So redirect_to is not available.

Checking for redirect is not supported in rspec-rails request specs, but is supported in Rails integration tests.

Whether you should explicitly check for how redirect was made (that it is was a 301 response and not a 307 response and not some javascript) is completely up to you.

Solution 5

Here is hackish solution that i found

# spec/features/user_confirmation_feature.rb

feature 'User confirmation' do
  scenario 'provide confirmation and redirect' do
    visit "/users/123/confirm"

    expect(page).to have_content('Please enter the confirmation code')
    find("input[id$='confirmation_code']").set '1234'

    do_not_follow_redirect do
      click_button('Verify')
      expect(page.driver.status_code).to eq(302)
      expect(page.driver.browser.last_response['Location']).to match(/\/en\//[^\/]+\/edit$/)
    end
  end

  protected

  # Capybara won't follow redirects
  def do_not_follow_redirect &block
    begin
      options = page.driver.instance_variable_get(:@options)
      prev_value = options[:follow_redirects]
      options[:follow_redirects] = false

      yield
    ensure
      options[:follow_redirects] = prev_value
    end
  end
end
Share:
32,926

Related videos on Youtube

Mohamad
Author by

Mohamad

I love well designed digital products, programming, and tech. Driving digital product roadmap in Brazil for world’s third largest retailer of home decor and building materials.

Updated on June 13, 2020

Comments

  • Mohamad
    Mohamad almost 4 years

    I don't know what I'm doing wrong, but every time I try to test for a redirect, I get this error: "@request must be an ActionDispatch::Request"

    context "as non-signed in user" do
      it "should redirect to the login page" do
        expect { visit admin_account_url(account, host: get_host(account)) }.to redirect_to(signin_path)
      end
    end
    1) AdminAccountPages Admin::Accounts#show as non-signed in user should redirect to the login page
         Failure/Error: expect { visit admin_account_url(account, host: get_host(account)) }.to redirect_to(signin_path)
         ArgumentError:
           @request must be an ActionDispatch::Request
         # ./spec/requests/admin_account_pages_spec.rb:16:in `block (4 levels) in <top (required)>'
    

    I'm using RSpec-rails (2.9.0) with Capybara (1.1.2) and Rails 3.2. I would appreciate it if someone could also explain why this is happening; why can't I use the expect in such a way?

    • Joseph Weissman
      Joseph Weissman almost 12 years
      Maybe I'm missing something, but what's wrong with assert_redirected_to?
    • Mohamad
      Mohamad almost 12 years
      @JosephWeissman, I get the same error!
  • bjnord
    bjnord over 7 years
    With newer versions of Capybara this no longer works. I have 2.10.1 and there is a new method have_current_path that can be used: expect(page).to have_current_path(new_user_path)
  • Rennan Oliveira
    Rennan Oliveira almost 7 years
    This helps if your redirect happens to be an external link
  • sekmo
    sekmo over 6 years
    which is the difference with expect(current_path).to eq('/login?status=invalid_token') ?
  • nruth
    nruth over 5 years
    @sekmo if you do that (as most of us used to) it uses the current value, which might not have stabilised yet, and you'll have a race condition in your test. When you use have_current_path it uses the capybara retry logic you're familiar with from dom assertions to retry the value until it matches or hits your timeout/retry limit and fails the test.
  • nruth
    nruth over 5 years
    which driver is this for?
  • randmin
    randmin about 4 years
    @bjnord Not true. current_path still works even in 3.13. You use a new syntax that comes with latest rspec and has nothing to do with capybara. See the answer of The Whiz of Oz ;)