5

我想将我的测试与内化隔离开来。我使用 rails 3.2.8 和 rspec 2.11.1

我把这段代码spec/support/translations.rb

module I18nHelpers
  def with_translations(locale, translations)
    I18n.backend.store_translations locale, translations
    yield
  ensure
    I18n.reload!
  end

结尾

RSpec.configure do |config|
  config.include I18nHelpers
end

然后我测试应用程序助手:

describe ApplicationHelper do
  context "messages" do
    it "show body" do
       with_translations :en, navigation: {messages: 'foo'} do
           concat messages_navigation
           assert_test 'span', 'foo'
       end
    end
  end
end

但是这个测试伴随着消息

Failure/Error: assert_select 'span', text: /foo/
     MiniTest::Assertion:
        </foo/> expected but was
        <"Messages">.

“消息”来自我真实的config/locales/en.yml 我从控制台测试#store_translations,它可以工作。但是当我在帮助模块中将行放在单词p I18n.t(translations.key.first)之前时ensure,它会向我显示旧的翻译。

谢谢你的帮助!

4

3 回答 3

5

要与您完全隔离,I18n您可能需要像这样切换后端:

def with_translations(locale, translations)
  original_backend = I18n.backend

  I18n.backend = I18n::Backend::KeyValue.new Hash.new, true
  I18n.backend.store_translations locale, translations

  yield
ensure
  I18n.backend = original_backend
end
于 2013-03-26T10:16:40.867 回答
3

我已经深入研究了这个问题,我想我有一个粗略的答案。问题似乎是由于在第一次调用之前 rails 不会在语言环境文件中加载翻译I18n.t

这有点旧,但仍然相关:

https://groups.google.com/forum/?fromgroups=#!msg/rails-i18n/QFe0GDVRIa0/G7K09NAgqJMJ

我们已经考虑了很多不同的方法来解决这个问题,最终我们的解决方案是将实际的翻译加载推迟到最新的可能点。因此客户端(如 Rails)只会将翻译源注册到 I18n,而后端实际上会在需要查找第一个翻译时延迟加载它们。

所以基本上,现有的翻译只会在你调用I18n.t. 当我注意到非常奇怪的行为时,我在控制台中玩耍时发现了这一点:

irb(main):001:0> def with_translations(locale, translations)
irb(main):002:1>   I18n.backend.store_translations locale, translations
irb(main):003:1>   yield
irb(main):004:1> ensure
irb(main):005:1*   I18n.reload!
irb(main):006:1> end
=> nil
irb(main):007:0> I18n.reload!
=> false
irb(main):008:0> with_translations(:en, { :messages => "foo" }) { puts I18n.t :messages }
Messages
=> nil
irb(main):009:0> I18n.t :some_other_string
=> "Some translation"
irb(main):010:0> with_translations(:en, { :messages => "foo" }) { puts I18n.t :messages }
foo
=> nil

ensure正在发生的事情与区块无关。我们可以删除它,同样的事情也会发生:

irb(main):011:0> I18n.reload!
=> false
irb(main):012:0> def with_translations(locale, translations)
irb(main):013:1>   I18n.backend.store_translations locale, translations
irb(main):014:1>   yield
irb(main):015:1> end    
=> nil
irb(main):016:0> with_translations(:en, { :messages => "foo" }) { puts I18n.t :messages }
Messages

即使没有重新加载,它仍然会吐出原始字符串,因为调用中的翻译在被I18n.backend.store_translations调用时被原始字符串覆盖I18n.t。但是,如果您在上述代码I18n.t 之前调用,它将起作用(并留下翻译,因为没有ensure重新加载翻译的块)。

所以基本上,答案是在覆盖您要更改的翻译之前I18n.t调用一个实际的翻译字符串来触发 Rails 加载现有翻译:

def with_translations(locale, translations)
  I18n.t translations.keys.first         # to make sure translations are loaded
  I18n.backend.store_translations locale, translations
  yield
ensure
  I18n.reload!
end

我只在控制台中尝试过这个,但我相信它也应该适用于你的 rspec 测试。如果有人对这个问题有更深入的了解,我很想听听他们的意见。

于 2012-10-22T01:18:03.700 回答
0

在遇到类似问题后,我最终写了一个这样的助手:

帮手

module TranslationSupport
  def with_translation(replacement_translation)
    I18n.t(".").deep_merge!(replacement_translation)
    begin
      yield
    ensure
      I18n.reload!
    end
  end
end

这里的快捷方式是先初始化后端I18n.t("."),然后合并所需的值。

正如其他答案所暗示的那样,或者可以初始化 i18n 后端,然后用于I18n.backend.store_translations更新值。这种方法的缺点是需要将语言环境作为参数传递。

用法

然后这个助手可以像这样使用它:

require "rails_helper"
require "support/translation"

RSpec.describe "testing a view with overridden translations" do
  include TranslationSupport

  it "works" do
    translations = {
      homepage: {
        index: {
          label: "Overridden"
        }
      }
    }

    with_translation(translations) do
      # Your action
    end

    # Expectations
  end
end

如果您不想每次都包含帮助程序:

RSpec.configure do |config|
  config.include TranslationSupport
end
于 2017-07-04T16:51:22.583 回答