Read more

Test redirects to an external URL with Cucumber/Capybara

Henning Koch
March 21, 2013Software engineer at makandra GmbH

When a controller action redirects to an external URL (like http://somehost.com/some/path) you will find that this is hard to test with Cucumber and Capybara:

  • A non-Javascript Rack::Test scenario will just ignore the host and try to open /some/path in your local application
  • A Selenium test will actually follow the redirect, which you probably don't want either
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

There are two workarounds for this. You can use either, or a combination of both.

  1. Write a controller spec

Controller specs can test if a response is a redirect:

describe FooContoller do

  describe '#redirecting_action' do

    it 'should redirect to somehost.com' do
      get :redirecting_action
      response.should redirect_to('http://somehost.com/some/path')
    end

  end

end
  1. Make the redirect observable from Cucumber

A hack is to write the destination URL to the screen instead of actually redirecting (only in integration tests). This way Capybara can "see" the destination URL with a regular page.has_content?('http://....').

To do this, in your redirecting action, use observable_redirect_to instead of redirect_to:

class FooController < ApplicationController

  def redirecting_action
    observable_redirect_to('http://somehost.com/some/path')
  end
  
end

To get observable_redirect_to, add these helper methods to your ApplicationController:

class ApplicationController < ActionController::Base

  private

  def integration_test?
    Rails.env.test? && defined?(Cucumber::Rails)
  end

  def observable_redirect_to(url)
    if integration_test?
      render :text => "If this wasn't an integration test, you'd be redirected to: #{url}"
    else
      redirect_to url
    end
  end

end

In your Cucumber feature, you can now say:

Then I should be redirected to the external site "http://somehost.com/some/path"

This step is implemented with this step definition:

Then(/^I should be redirected to the external site "(.*?)"$/) do |url|
  page.should have_content("If this wasn't an integration test, you'd be redirected to: #{url}")
end
Henning Koch
March 21, 2013Software engineer at makandra GmbH
Posted by Henning Koch to makandra dev (2013-03-21 15:03)