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