【问题标题】:Minitest tests fail with custom HTTP headers in Rails 4.2在 Rails 4.2 中,Minitest 测试因自定义 HTTP 标头而失败
【发布时间】:2015-12-26 12:30:31
【问题描述】:

我无法使用 minitest 在我的 Rails 4.2 应用程序中成功测试自定义 HTTP 变量——出于某种原因,Rails 似乎根本无法识别它们。

我有以下测试:

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

以及对应的控制器:

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

最后两个测试失败,因为 Rails 似乎无法识别我设置的 HTTP 标头变量,并且一切都进入了默认语言环境:

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

如果我将 Accept-Language 更改为 HTTP_ACCEPT_LANGUAGE那个测试然后通过。

根据Rails documentation,我的测试应该可以工作。我用谷歌搜索了这个问题几种不同的方法来寻找答案,结果空无一物。 Stack Overflow 上有几个与此类似的问题,但这些问题的答案或建议都没有解决我的问题。

我错过了什么?

环境:

  • Mac OS X 10.11.1
  • Ruby 2.2.2
  • Rails 4.2.4
  • minitest 5.8.0

【问题讨论】:

    标签: ruby-on-rails minitest ruby-on-rails-4.2


    【解决方案1】:

    Rails 通过以下方式为自定义 HTTP 标头设置 request.env 变量:

    • 将标题全部转换为大写
    • 用下划线替换破折号
    • 前置“HTTP_”

    因此您可以在测试中设置request.headers["Accept-Language"],并在控制器中使用request.env["HTTP_ACCEPT_LANGUAGE"] 访问该值。同样,您在测试中设置request.headers["CloudFront-Viewer-Country"],并在控制器中使用request.env["HTTP_CLOUDFRONT_VIEWER_COUNTRY"] 访问该值。

    【讨论】:

      【解决方案2】:

      Rails 测试指南告诉我们Setting Headers

      "HTTP headers 和 CGI​​ 变量可以直接在@request 实例变量上设置:..."

      所以你需要这样写:

      @request.headers["Accept-Language"] = "zh-cn, en-us"
      

      我认为最好不要在before方法中设置标题,而是直接在测试中。因此,每次您只设置此测试所需的标题。而且您不需要先设置cookie,然后将其删除。

      【讨论】:

      • 这与我在帖子中引用的文档相同,但它不起作用(有或没有@)。我不只是测试单独设置语言环境的每种方法,我想测试它们发生的 order ——就像一个后备链(即,URL 参数总是优先。如果那不可用,那么 cookie 总是优先。如果这些都不可用,则 Accept-Language 标头优先,等等)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多