26
votes

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?

5
Maybe I'm missing something, but what's wrong with assert_redirected_to?Joseph Weissman
@JosephWeissman, I get the same error!Mohamad

5 Answers

42
votes

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')
20
votes

you can do it this way:

expect(current_path).to eql(new_app_user_registration_path)
14
votes

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.

11
votes

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.

7
votes

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