How to set locale default_url_options for functional tests (Rails)
Asked Answered
W

12

14

In my application_controller, I have the following set to include the locale with all paths generated by url_for:

  def default_url_options(options={})
    { :locale => I18n.locale }
  end

My resource routes then have a :path_prefix = "/:locale"

Works fine on the site.

But when it comes to my functional tests, the :locale is not passed with the generated urls, and therefore they all fail. I can get around it by adding the locale to the url in my tests, like so:

  get :new, :locale => 'en'

But I don't want to have to manually add the locale to every functional test.

I tried adding the default_url_options def above to test_helper, but it seems to have no effect.

Is there any way I can change the default_url_options to include the locale for all my tests?

Thanks.

Weymouth answered 31/12, 2009 at 22:38 Comment(0)
A
2

Looking through how the controller test case generates the url there doesn't seem to be a direct way to have it use the defualt_url_options. The main block that actually does the url creationg (in the tests) looks like this (http://github.com/rails/rails/blob/master/actionpack/lib/action_controller/test_case.rb):

private
  def build_request_uri(action, parameters)
    unless @request.env['REQUEST_URI']
      options = @controller.__send__(:rewrite_options, parameters)
      options.update(:only_path => true, :action => action)

      url = ActionController::UrlRewriter.new(@request, parameters)
      @request.request_uri = url.rewrite(options)
    end
  end

This gets called by the process method which is in turn called by the get, post, head, or put methods. One way to maybe get what you are looking for might be to alias_chain the process method.

class ActionController::TestCase
  def process_with_default_locale(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
    parameters = {:locale=>'en'}.merge(parameters||{})
    process_without_default_locale(action, parameters, session, flash, http_method)
  end
  alias_method_chain :process, :default_locale
end

You'll want to put that into your test helper, outside of the TestCase class I think. Let me know how it works for you, I haven't really tested it out so we'll see.

Audie answered 31/12, 2009 at 23:50 Comment(4)
Thanks, John. That solved the specific case above and seems to work great! What it doesn't seem to solve is how TestCase interprets named routes, whether they're in the test itself, or used in the application. For example, if I have in a test: redirect_to homepage_url and homepath_url expects the locale (map.homepage '/:locale', :controller => 'home'), I get an error, when running the test, that the url requirements are not satisfield (missing "locale"). Again, the application runs fine as default_url_options takes care of adding the locale. Any way to fix that?Weymouth
Let me add that if this was only a problem in the test themselves, it wouldn't be so bad. I can just add the locale whenever used a named route in a test. But the problem is that it also generates an error whenever a named route is used in my application (and I don't want to have to change all that or specify the locale each time just so my tests will run).Weymouth
Ah, I didn't realize it was happening in your app as well. I just came across a lighthouse ticket that might help you out a little bit (seems to still be an issue but there are some work arounds) rails.lighthouseapp.com/projects/8994/tickets/…Audie
Other than that it's beyond me, might be worthwhile to ask another question specifically about the named routes and default_url_options not working in the app and see if anyone has any ideas.Audie
M
8

For Rails 5, I found this simple solution In test_helper.rb based on action_dispatch/testing/integration.rb

module ActionDispatch::Integration
  class Session
    def default_url_options
      { locale: I18n.locale }
    end
  end
end
Malti answered 14/11, 2016 at 20:18 Comment(2)
Thanks for this answer, it worked for me. Although I was wondering if there is any way to use a customize default_url_options from the ApplicationController, because it doesn't make any sense to write twice the same configuration.Fraze
I would just add than maybe it's better to use the options as defined in ApplicationController, for consistence: ApplicationController.new.send(:default_url_options)Homeroom
J
7

In the Rails 3.1-stable branch, the process method is now within a Behavior module. So here is the code which worked for me (slightly different from John Duff's answer):

class ActionController::TestCase

  module Behavior
    def process_with_default_locale(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
      parameters = { :locale => I18n.default_locale }.merge( parameters || {} )
      process_without_default_locale(action, parameters, session, flash, http_method)
    end 
    alias_method_chain :process, :default_locale
  end 
end

And I made sure this code gets called before running the specs/tests. A good place to put it is in the test_helper class.

Jimjimdandy answered 19/1, 2012 at 2:15 Comment(2)
Martin Carel lays out this method on ruby-forum.com/topic/3448797 he also adds another method: module ActionDispatch::Assertions::RoutingAssertions def assert_recognizes_with_default_locale(expected_options, path, extras={}, message=nil) expected_options = { :locale => I18n.default_locale.to_s }.merge(expected_options || {} ) assert_recognizes_without_default_locale(expected_options, path, extras, message) end alias_method_chain :assert_recognizes, :default_locale endCrenation
I also said that modifying the assert_recognizes method was a cheat. Indeed, I don't think it's a good idea to hack your testing tools like that, as opposed to mock the code you're trying to test (as is the case with adding a method to the Behavior module).Jimjimdandy
C
5

I tried a lot of examples, but only this one helped me. It is concise and simple. Add this code snippet to the test.rb:

Rails.application.configure do
  # ... other config ...

  routes.default_url_options[:locale] = :en
end

Works on Rails 5.1.4

Corporeal answered 28/2, 2019 at 12:0 Comment(3)
Can confirm that this works on Rails 6.0.0 as well.Revalue
Worked for me as well with Rails 6, I put it into test_helper.rb inside the ActiveSupport::TestCase class.Pusher
Confirm this works for controller & integration tests (and is still neccesary) as of Rails 7.1. However there are still issues with system tests picking up the default_url_options correctly.Birch
A
3

In case anyone is using this with Rails 4.0, the order of arguments in the process method has changed, so you'll need to use this updated patch (based on @martin-carel's answer, just with the http_method argument moved to the second argument):

class ActionController::TestCase
  module Behavior
    def process_with_default_locale(action, http_method = 'GET', parameters = nil, session = nil, flash = nil)
      parameters = { :locale => I18n.locale }.merge( parameters || {} ) unless I18n.locale.nil?
      process_without_default_locale(action, http_method, parameters, session, flash)
    end
    alias_method_chain :process, :default_locale
  end
end

Hope that helps anyone stuck on this problem.

Aircool answered 29/9, 2013 at 14:17 Comment(0)
A
2

Looking through how the controller test case generates the url there doesn't seem to be a direct way to have it use the defualt_url_options. The main block that actually does the url creationg (in the tests) looks like this (http://github.com/rails/rails/blob/master/actionpack/lib/action_controller/test_case.rb):

private
  def build_request_uri(action, parameters)
    unless @request.env['REQUEST_URI']
      options = @controller.__send__(:rewrite_options, parameters)
      options.update(:only_path => true, :action => action)

      url = ActionController::UrlRewriter.new(@request, parameters)
      @request.request_uri = url.rewrite(options)
    end
  end

This gets called by the process method which is in turn called by the get, post, head, or put methods. One way to maybe get what you are looking for might be to alias_chain the process method.

class ActionController::TestCase
  def process_with_default_locale(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
    parameters = {:locale=>'en'}.merge(parameters||{})
    process_without_default_locale(action, parameters, session, flash, http_method)
  end
  alias_method_chain :process, :default_locale
end

You'll want to put that into your test helper, outside of the TestCase class I think. Let me know how it works for you, I haven't really tested it out so we'll see.

Audie answered 31/12, 2009 at 23:50 Comment(4)
Thanks, John. That solved the specific case above and seems to work great! What it doesn't seem to solve is how TestCase interprets named routes, whether they're in the test itself, or used in the application. For example, if I have in a test: redirect_to homepage_url and homepath_url expects the locale (map.homepage '/:locale', :controller => 'home'), I get an error, when running the test, that the url requirements are not satisfield (missing "locale"). Again, the application runs fine as default_url_options takes care of adding the locale. Any way to fix that?Weymouth
Let me add that if this was only a problem in the test themselves, it wouldn't be so bad. I can just add the locale whenever used a named route in a test. But the problem is that it also generates an error whenever a named route is used in my application (and I don't want to have to change all that or specify the locale each time just so my tests will run).Weymouth
Ah, I didn't realize it was happening in your app as well. I just came across a lighthouse ticket that might help you out a little bit (seems to still be an issue but there are some work arounds) rails.lighthouseapp.com/projects/8994/tickets/…Audie
Other than that it's beyond me, might be worthwhile to ask another question specifically about the named routes and default_url_options not working in the app and see if anyone has any ideas.Audie
L
2

alias_method_chain is deprecated in Rails 5, and it seems the Behavior process method has changed.

Here's my modification of Martin Carel's answer above, adapted to Rails 5.

RSpec.configure do |config|
  module ActionController
    class TestCase
      module Behavior
        module LocaleParameter
          def process(action, parameters = {params: {}})
            unless I18n.locale.nil?
              parameters[:params][:locale] = I18n.locale
            end

            super(action, parameters)
          end
        end

        prepend Behavior::LocaleParameter

      end
    end
  end
end

I'm by no means an expert in Rails or Ruby, so if something can be improved in this answer, let me know and I'll change it.

Longspur answered 6/9, 2016 at 13:41 Comment(2)
Yes!! this is the solution, and adding to each Test I want to define a different locale before :context do I18n.locale = :ca end I can test whatever locale I need for each circunstance. Using rails 5.1.4.rc1Furey
I added if parameters[:params].nil? parameters[:params]={} end before unless I18n.locale.nil?.... to avoid crash for request without parametersFurey
T
1

I ran into this problem with a failing cucumber test. I use locales as parameters in the url, i.e. http://mysite.com/home?locale=he

What I do to cope with this is to drop all locale related stuff from the url during testing by defining default_url_options depending on the environment I use:

  # app/controllers/application_controller.rb
  def default_url_options(options={})
    logger.debug "default_url_options is passed options: #{options.inspect}\n"
    ENV["RAILS_ENV"] != "cucumber" ? { :locale => I18n.locale } : {}
  end
Tradesman answered 21/8, 2010 at 21:3 Comment(1)
I'm not sure if this exposes certain edge cases, but it seems like an otherwise elegant solution to me.Wither
B
1

I've come up with a bit less invasive solution to this problem.

setup :set_default_locale 

def set_default_locale
  def @request.query_parameters
    {:locale => "en"}.merge(@query_parameters)
  end
end

The advantage of this solution is that it means you can only apply the default locale parameter to certain test cases, so you can test, for example, the redirection strategy itself.

Bottomry answered 17/11, 2010 at 6:54 Comment(0)
T
1

Things changed again with Ruby 5.1. Here's what worked for me:

class ActionController::TestCase
  module Behavior
    module LocaleParameter
      def process(action, params: {}, **args)
        # Locale parameter must be a string
        params[:locale] = I18n.locale.to_s
        super(action, params: params, **args)
      end
    end
  end
  prepend Behavior::LocaleParameter
end
Tobolsk answered 9/6, 2017 at 20:57 Comment(0)
A
1

default_url_options are not used when processing functional tests and it doesn't seem there is a way to define defaults without monkey patching (master branch from October 15th 2021).

So I managed to get it working by overwriting the ActionController::TestCase#process method within the test/test_helper.rb file as follows:

class ActionController::TestCase
  module DefaultParameters
    def process(action, params: {}, **args)
      params[:locale] ||= 'en'
      super(action, params: params, **args)
    end
  end
  prepend DefaultParameters
end

The above code works with Rails 6.1.4 but might need some adjustments for earlier or future Rails versions.

Awning answered 15/10, 2021 at 9:56 Comment(0)
I
0

With integration testing, I found that the above monkey patch needs to be different, this is what worked for me (Rails 2.3.4):

class ActionController::Integration::Session
  def url_for_with_default_locale(options)
    options = { :locale => 'en' }.merge(options)
    url_for_without_default_locale(options)
  end
  alias_method_chain :url_for, :default_locale
end
Inconsiderate answered 2/4, 2010 at 6:15 Comment(0)
B
0

This is still tricky, but fortunately ultimately much more straightforward to solve in Rails 7.

Controller/Integration tests can be made to work correctly by adding this line in config/environments/test.rb:

Rails.application.routes.default_url_options[:locale] = I18n.default_locale

Or by adding this in test_helper.rb:

class ActiveSupport::TestCase
  setup do
    Rails.application.routes.default_url_options[:locale] = I18n.default_locale
  end

However, neither of the above solutions solve the issue for system tests. To get system tests working correctly, add this into application_system_test_case.rb:

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  ...

  setup do
    default_url_options[:locale] = I18n.default_locale
  end
Birch answered 16/5, 2024 at 23:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.