Saying stuff about stuff.

Dynamically setting default_url_options in Capybara

If you’re developing a full-stack Rails app with a link-based hypermedia API then you may find incorrect URLs breaking your system/feature Capybara specs. What’s going on?

When running your tests Capybara lazily boots the Rails app on a random port and, because this host/port are unknown to Rails, links generated in serializers (and emails) will point to the wrong URL – and following them within your test app and Capybara will fail. Just like dynamically setting Rails default_url_options in Heroku review apps you must tell Rails the host/port on which to build these URLs.

To do this with RSpec you can use RSpec.shared_context to update default_url_options before the example runs and reset it after. Add a support file in spec/support/default_url_options.rb with the following:

original_host = Rails.application.routes.default_url_options[:host]
original_port = Rails.application.routes.default_url_options[:port]

RSpec.shared_context 'default_url_options' do
  before do
    Rails.application.routes.default_url_options[:host] = Capybara.current_session.server.host
    Rails.application.routes.default_url_options[:port] = Capybara.current_session.server.port
  end

  after do
    Rails.application.routes.default_url_options[:host] = original_host
    Rails.application.routes.default_url_options[:port] = original_port
  end
end

(For a reason unknown to me I had to use separate before/after blocks instead of an around block.)

Include it in your browser specs in spec/rails_helper.rb:

RSpec.configure do |config|
  # Traditional feature specs.
  config.include_context 'default_url_options', js: true, type: :feature

  # New fangled system tests.
  config.include_context 'default_url_options', type: :system
end

Now all your _links will point to the correct host/port and you can get on with consuming them in your hypermedia link-driven single page app Rails monolith.