使用Il8n gem

时间:2015-11-26 07:04:38

标签: ruby-on-rails ruby rspec internationalization

我正在开发一个描述生成器功能,它将查看对象属性并通过从已设置的各种语言环境(多种语言)中提取字符串来填充描述。

我有以下代码

module Rooms
  class DescriptionGenerator
  attr_reader :room, :locale

  def initialize(room, locale="en")
    @room = room
    @locale = locale
  end

  private

  def t(key, options={})
    I18n.t("rooms.description_generator.#{key}", options.merge({ locale: locale }))
  end

  def wifi
    t("wifi", room_type: room.room_type.type).values.sample if room.wifi
  end

然后我进行了以下测试:

describe "#wifi" do
  let!(:room_with_wifi) { create(:room, :visible, wifi: true) }
  let!(:room) { create(:room) }

  #This one is failing
  it "returns wifi sentence if room has wifi" do
    sentence = I18n.t('rooms.description_generator.wifi', room_type: room_with_wifi.room_type.type).values.sample
    expect(Rooms::DescriptionGenerator.new(room_with_wifi,"en").send(:wifi)).to eq (sentence)
  end

  it "returns nil if room does not have wifi" do
    expect(Rooms::DescriptionGenerator.new(room,"en").send(:wifi)).to eq nil
  end
end

我的问题是"考虑到使用样本,最好的方法是什么?"

我的原始解决方案(我不建议不推荐)是添加:

class DescriptionGenerator
  def sample(arr)
    if Rails.env.test?
      arr.values.first
    else
      arr.sample.values
    end
  end
end

这个“强制执行”' RSpec会从我的语言环境中选择第一个选项:

three_positive_reviews:
  a: "This is not so great"
  b: "This %{string_for_interpolation} is great."
  c: "This is bad"

直到我添加了一个要插入到测试中使用的第一个(a)中的字符串并且意识到在i18n gem中不支持数组内插值(Interpolation in I18n array)之前,这一切都在通过。

所以我重构了:

def t(key, options={})
  value = I18n.t("rooms.description_generator.#{key}")
  key = "#{key}.#{sample(value.keys)}" if value.is_a?(Hash)
  I18n.t("rooms.description_generator.#{key}", options.merge({ locale: locale }))
end

def sample(keys)
  if Rails.env.test?
    keys.first
  else
    keys.sample
  end
end

这现在通过了我的测试套件 - 但是在一些细节上看了这个想要寻求更优化的解决方案&我是如何在没有线

的情况下更适当地测试(可能是通过存根样本??)
if Rails.env.test?

2 个答案:

答案 0 :(得分:1)

我会做以下(我没有运行代码,想到):

let(:room) { create(:room) }
let(:locale) { 'en' }
let(:generator) { Rooms::DescriptionGenerator.new(room, locale) } 

describe '#wifi' do
  let(:wifi_sentences) do
    I18n.t('rooms.description_generator.wifi', locale: locale)).values
  end

  context 'when the room has wifi' do
    it 'returns wifi sentence' do
      expect(generator.send(:wifi)).to be_in(wifi_sentences)
    end
  end

  context 'when the room has no wifi' do
    before do
      allow(room).to receive(:wifi).and_return(false) # or nil, don't know
    end

    it 'returns nil' do
      expect(generator.send(:wifi)).to be(nil)
    end
  end
end

希望这会有所帮助:)

答案 1 :(得分:0)

感谢Victor的回答 - 非常有助于重构我的测试。我想发布我的最终解决方案,将一些调整考虑在内,以防对其他人有用。

let(:room) { create(:room) }
let(:locale) { 'en' }
let(:generator) { Rooms::DescriptionGenerator.new(room, locale) }

describe "#laundry" do
  it "returns wifi sentence if room has wifi" do
    sentence = "This apartment is equipped with laundry facilities."
    room.laundry = true
    expect(generator.send(:laundry)).to eq(sentence)
  end

  it "returns nil if room is not have laundry" do
    expect(generator.send(:laundry)).to eq nil
  end
end

这适用于简单的一个选项区域设置:

  laundry: "This %{room_type} is equipped with laundry facilities."

然而,由于Rails i18n无法在数组中插入字符串(即,当提供了多个键值对时),我会对样本进行处理。

context "translations with multiple options that require interpolation" do
  before do
    allow(generator).to receive(:sample) { |keys| keys.first }
  end

  describe "#three_positive_reviews" do

   context 'when the room has 3 positive reviews' do
     before do
       3.times do
         create(:feedback, :non_external, :positive, room: room)
       end
     end

    it "returns three_positive_reviews sentence" do
      sentence = "This apartment is highly reviewed"
      expect(generator.send(:three_positive_reviews)).to eq(sentence)
    end
  end

  context 'when the room does not have 3 positive reviews' do
    it "returns nil" do
      expect(generator.send(:three_positive_reviews)).to eq nil
    end
  end

然后能够在我的代码中将我的示例方法重构为:

def sample(keys)
  keys.sample
end

这对我来说似乎比我原来在问题中发布的更清晰。