Testing domains with Rails and Test-Unit

I'm currently working on a Ruby on Rails project where sub-domains come into play pretty heavily. Each account created on the site has its own sub-domain, which means that there is no pre-set list. I ran into some problems when writing functional tests and integration tests with Capybara, as I had no idea how to set the current domain and assert a change in sub-domain. Fortunately, after struggling through for a couple of hours, I found the solution and I'm putting it here for the benefit of anyone else!

Setting the domain in functional tests

To set the domain in test-unit tests, use the @request.host variable inside the test method:

# test/functional/whatever.rb
test "whatever" do
  @request.host = "my.domain.com"
  #... rest of test
end

I was doing this on an account basis, and quite regularly, so I created a helper method to set the domain using the account, and monkey-patched the ActionController::TestCase class:

# test/test_helper.rb
class ActionController::TestCase
  def use_account(account)
    account.save unless account.persisted?
    @request.host = "#{account.subdomain}.domain.com"
  end
end
# test/functional/accounts_controller.rb
class AccountsControllerTest < ActionController::TestCase
  test "something to do with an account" do
    account = FactoryGirl.create :account
    use_account account
    #... rest of test
  end
end

Setting the domain in integration tests with Capybara

If you aren't using Capybara, you can simply set the current host in integration tests with the host! method:

# test/integration/domain.rb
class DomainTest < ActionDispatch::IntegrationTest
  test "something without capybara" do
    host! "my.domain.com"
    #... rest of test
  end
end

However, if using Capybara, you also need to set the host (including protocol, http://) with:

# test/integration/domain.rb
class DomainTest < ActionDispatch::IntegrationTest
  test "something with capybara" do
    Capybara.app_host = "http://my.domain.com"
    #... rest of test
  end
end

Again, I monkey-patched ActionDispatch::IntegrationTest to provide a helper method:

# test/test_helper.rb
class ActionDispatch::IntegrationTest
  def use_account(account)
    account.save unless account.persisted?
    the_host = "#{account.subdomain}.domain.com"
    host! the_host
    Capybara.app_host = "http://#{the_host}"
  end
end

Testing domain changes in integration tests

For non-Capybara tests, you can simply use assert_redirected_to:

# test/integration/domain.rb
class DomainTest < ActionDispatch::IntegrationTest
  test "bad subdomain redirects to main domain" do
    host! 'lkjasdasd.domain.com'
    get root_path
    assert_redirected_to 'http://domain.com'
  end
end

When using Capybara, you have access to current_url:

# test/integration/domain.rb
class DomainTest < ActionDispatch::IntegrationTest
  test "bad subdomain redirects to main domain with Capybara" do
    Capybara.app_host = 'http://lkjasdasd.domain.com'
    visit root_path
    assert_equal 'http://domain.com/', current_url
  end
end
← Previous post: Uploading files in functional tests with Rails, Paperclip and Test Unit Next post: Reset PostgreSQL auto increment value in Rails →
comments powered by Disqus