Adding subdomains to a Rails application is an easy task today, but things can get a little trickier when testing is involved.
For starters, you may only need to handle subdomains at the routing level, which is quite easy. But after the easy step some troubles may be awaiting for you.
In our example we want to add an “admin” subdomain to a brand new app. We edit the file routes.rb as follows and we’re basically done:
# myappname/config/routes.rb
Rails.application.routes.draw do
constraints subdomain: 'admin' do
root to: 'admin#index', as: 'admin_root' # will match http://admin.myappname.dev/
end
end
Of course, to make the thing actually work you also need an AdminController
with an index
template.
Want to see the page in the browser? You can’t yet. If you start the server and go to admin.localhost:3000 you will only be able to see the default rails page. You have a few options in order to make this work, let’s discuss them one by one
Lvh.me
Just visit http://admin.lvh.me:3000/
and you’ll see the admin page. It works because lvh.me
is an external website that points to localhost/127.0.0.1.
This solution is ok as long as you have an internet connection and you are willing to pay the time overhead of the necessary DNS search plus redirect.
Edit /etc/hosts
In order to avoid hitting an external website the easiest solution is to add to your /etc/hosts
file an entry like this:
127.0.0.1 myappname.dev
127.0.0.1 admin.myappname.dev
and your app will be available at http://admin.myappname.dev:3000
. Drawbacks are you need an entry for every single subdomain of each application and you need to manually edit/keep updated the file.
Pow
The most flexible solution is to use Pow or equivalents (if you’re on Linux you can use Prax). For increased ease of use you can manage pow with the gem powder. If you hacked the /etc/hosts
file you should revert the changes before continuing.
Now that pow has taken control of your computer (!) if you visit http://myappname.dev
(no port required, defaults to 80) you will see Pow page with the instructions. Just follow them (you’re just a symlink away) and you’re done.
Testing subdomains with Capybara
If good coders always test their code, then we’re no exception. You may want to write integration tests for your admin page, so the names Rspec and Capybara should be ‘nuff said. I’m assuming you already configured Rspec and Capybara in this app.
RackTest webdriver
I’m going to use a single spec to test the admin page, which will evolve through various steps according to increasing requirements.
Let’s add a very basic spec that uses Capybara’s default driver, rack_test
, which is the fastest/easiest to work with:
# myappname/spec/features/admin_page_spec.rb
require 'rails_helper'
describe 'the admin page' do
it 'can be seen' do
visit admin_root_path
expect(page).to have_content 'Admin#index'
end
end
The spec won’t pass. You need to add as first line of the test the follwing code:
Capybara.default_host = 'http://admin.myappname-test.dev'
where myappname-test
can actually be any name you want. Running specs again should result in a green message.
Selenium webdriver
I’m assuming you have already configured rspec/capybara to use Selenium in the app. To trigger Selenium in the previous spec we only need to add :js
to the it
description:
# myappname/spec/features/admin_page_spec.rb
require 'rails_helper'
describe 'the admin page', :js do
it 'can be seen' do
Capybara.default_host = 'http://admin.myappname-test.dev'
visit admin_root_path
expect(page).to have_content 'Admin#index'
end
end
After the change the spec doesn’t pass anymore. We need to add some more configuration:
# myappname/spec/features/admin_page_spec.rb
require 'rails_helper'
describe 'the admin page', :js do
it 'can be seen' do
Capybara.server_port = 3030
Capybara.default_host = 'http://myappname-test.dev'
Capybara.app_host = 'http://admin.myappname-test.dev'
visit admin_root_path
expect(page).to have_content 'Admin#index'
end
end
but we’re not done yet. We also need to add some more configuration for Pow. Let’s create a new file in ~/.pow
named myappname-test
that contains the text 3030
:
cd ~/.pow && echo "3030" > myappname-test
At this point rake spec
should run without errors.
Travis integration
Here at Mikamai we use Travis for continuous integration, so the final step before calling a day it’s to push the code and see if the specs pass on the CI server as well.
First, the project must be configured to allow Selenium to work on Travis. This link explains the basics. You probably just need to add this to your .travis.yml
file:
# myappname/.travis.yml
before_script:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- sleep 3 # give xvfb some time to start
At this point we need to instruct travis to use subdomains by adding the following configuration to the .travis.yml file:
# myappname/.travis.yml
addons:
hosts:
- admin.myappname-test.dev
- myappname-test.dev
Again, let’s also add some further configuration to our rspec test to make it pass on Travis:
# myappname/spec/features/admin_page_spec.rb
require 'rails_helper'
describe 'the admin page', :js do
it 'can be seen' do
Capybara.server_port = 3030 unless ENV['CI'] # don't use port 3030 on travis
Capybara.always_include_port = true # for travis
Capybara.default_host = 'http://myappname-test.dev'
Capybara.app_host = 'http://admin.myappname-test.dev' # to change the subdomain
visit admin_root_path # this route is under under "admin" subdomain
expect(page).to have_content 'Admin#index'
end
end
Now, when you push your code to Travis you should be rewarded with a green outcome… job done!
Of course there is one more step that should be done: move the Capybara port/domains initialization into rspec configuration:
# rspec/rails_helper.rb
Rspec.configure do |config|
Capybara.server_port = 3030 unless ENV['CI'] # don't use port 3030 on travis
Capybara.always_include_port = true # for travis
Capybara.default_host = 'http://myappname-test.dev'
end
Leave a Reply