我正在开发一个描述生成器功能,它将查看对象属性并通过从已设置的各种语言环境(多种语言)中提取字符串来填充描述。
我有以下代码
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?
答案 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
这对我来说似乎比我原来在问题中发布的更清晰。