Using default_url_options in RSpec with Rails 5
There's a long-standing bug in the integration of controller testing into RSpec that prevents you from easily setting default_url_options
for your controller specs. As far as I can tell, it doesn't get fixed because the RSpec teams considers the problem a bug in Rails, and the Rails team does not care if RSpec breaks.
I'm talking about the issue you run into when you're trying to work with locale settings passed into your application as namespace variables in routes.rb
like this:
scope "/:locale" do
devise_for :users, #...and so on
Today I learned that the inability to set a default :locale
parameter can be maddening. Your specs will fail with ActionView::Template::Error: No route matches
errors:
1) Devise::RegistrationsController POST /users should allow registration
Failure/Error: %p= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token)
ActionView::Template::Error:
No route matches {"action":"show","confirmation_token":"pcyw_izS8GchnT-R3EGz","controller":"devise/confirmations"} missing required keys: [:locale]
The reason is that ActionController::TestCase
ignores normal settings of default_url_options
in ApplicationController
or your config/environments/test.rb
. No other intuitive attempt at a workaround worked either. Frustratingly, it took me around an hour to debug and come up with a monkeypatch-style workaround. The existing workarounds that I could find online are all broken in Rails 5.
So here it is:
# spec/support/fix_locales.rb
ActionController::TestCase::Behavior.module_eval do
alias_method :process_old, :process
def process(action, *args)
if params = args.first[:params]
params["locale"] = I18n.default_locale
end
process_old(action, *args)
end
end
Note the assumption that you are passing params in your spec using a symbol key and not the string "params"
.