I am unable to successfully test custom HTTP variables in my Rails 4.2 application using minitest—for some reason, Rails doesn't seem to recognize them at all.
I have the following test:
require "test_helper"
describe ApplicationController do
tests HomeController
before do
cookies[:locale] = "ru"
request.headers["Accept-Language"] = "zh-cn, en-us"
request.headers["CloudFront-Viewer-Country"] = "FR"
I18n.default_locale = :en
end
it "sets locale from URL params first" do
get :index, locale: "de"
I18n.locale.must_equal :de
end
it "sets locale from a cookie second" do
get :index
I18n.locale.must_equal :ru
end
it "sets locale from Accept-Language header third" do
cookies.delete :locale
get :index
I18n.locale.must_equal :"zh-CN"
end
it "sets locale from CloudFront-Viewer-Country header last" do
cookies.delete :locale
request.headers["Accept-Language"] = nil
get :index
I18n.locale.must_equal :fr
end
end
And the corresponding controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :set_locale
def default_url_options(options = {})
{ locale: I18n.locale }.merge options
end
private
def set_locale
I18n.locale = params[:locale] ||
cookies[:locale] ||
extract_locale_from_accept_language_header ||
set_locale_from_cloudfront_viewer_country ||
I18n.default_locale
unless request.fullpath =~ /^\/(?:[a-z]{2,2})(?:[-|_](?:[A-Z]{2,2}))?/
redirect_to %(#{request.protocol}#{request.host}/#{I18n.locale}#{request.fullpath unless request.fullpath == "/"}), status: :found
end
end
def extract_locale_from_accept_language_header
if request.env["Accept-Language"]
request.env["Accept-Language"].scan(/(?:[a-z]{2,2})(?:[-|_](?:[A-Z]{2,2}))?/i).first
end
end
def set_locale_from_cloudfront_viewer_country
case request.env["CloudFront-Viewer-Country"]
when "FR"
return :fr
else
return I18n.default_locale
end
end
end
The last two test fail because Rails doesn't seem to recognize the HTTP header variables that I set and everything falls through to the default locale:
Failure:
ApplicationController#test_0004_sets locale from CloudFront-Viewer-Country header last [/Users/aporter/Developer/com/wavetronix/www/test/controllers/application_controller_test.rb:33]
Minitest::Assertion: Expected: :fr
Actual: :en
Failure:
ApplicationController#test_0003_sets locale from Accept-Language header third [/Users/aporter/Developer/com/wavetronix/www/test/controllers/application_controller_test.rb:26]
Minitest::Assertion: Expected: :"zh-CN"
Actual: :en
If I change Accept-Language
to HTTP_ACCEPT_LANGUAGE
, that test then passes.
According to the Rails documentation, my tests should work. I Googled this problem several different ways looking for an answer and came up empty. There are a few questions similar to this one on Stack Overflow, but none of the answers or suggestions for those questions solve my problem.
What am I missing?
Environment:
Rails sets a request.env
variable for custom HTTP headers by:
So you can set request.headers["Accept-Language"]
in the test, and access that value with request.env["HTTP_ACCEPT_LANGUAGE"]
in the controller. Likewise, you set request.headers["CloudFront-Viewer-Country"]
in the test, and access that value with request.env["HTTP_CLOUDFRONT_VIEWER_COUNTRY"]
in the controller.
The Rails testing guide tell us about Setting Headers:
"HTTP headers and CGI variables can be set directly on the @request instance variable:..."
So you need to write it like this:
@request.headers["Accept-Language"] = "zh-cn, en-us"
I think it is better when you not set up the headers in the before
method, but directly in the test. So each time you set only the header you need for this test. And you don't need set the cookie first, and then delete it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With