Rails 引擎主机应用程序 类 在 RSpec 测试中
Rails Engine host application classes in RSpec tests
我有一个 Rails 引擎,其中包含 link 某些主机应用程序 类 的方法(我知道这种类型的耦合很糟糕,但在这种情况下它是不可避免)。
我需要测试使用主机 类 的方法,但在尝试 double/mock/stub 主机 类 时出现 uninitialized constant MyEngine::BaseUser
错误(BaseUser
或 Tutor
在这种情况下)。
我曾试图通过创建模拟 类 来解决这个问题,但我认为我留下的是一个坏主意,这意味着我的测试不太有用(见下文)。
有什么想法可以做得更好,或者有更好的方向建议吗?
正如我上面所说,我是这样(糟糕地)解决这个问题的:
BaseUser = Class.new do
attr_accessor :id
def initialize(id = 1)
@id = id
end
def self.find(id)
self.new(id)
end
def tutor
Tutor.find(self.id)
end
end
class Tutor
attr_accessor :id, :first_name
def initialize(id = 1)
@id = id
@first_name = "Tutor with ID #{id}'s first name"
end
def self.find(id)
self.new(id)
end
end
it 'returns the hosts first name' do
allow(MyEngine).to receive_message_chain(:user_class, :constantize) { BaseUser }
ai = FactoryGirl.create(:availability_interval, host_id: 1)
expect(ai.host_first_name).to eq BaseUser.find(1).tutor.first_name
end
我正在测试的方法如下所示:
def host_full_name
MyEngine.user_class.constantize.find(self.host_id).tutor.full_name
end
(MyEngine.user_class
是 "BaseUser")
您的引擎将所有内容命名为您的引擎。如果您正在尝试访问实际上在基本范围内定义的 class(即在您的引擎之外),您可以强制它在带有 ::
的基本范围内找到 class,例如:
expect(ai.host_first_name).to eq ::BaseUser.find(1).tutor.first_name
我在问题中所做的似乎仍然是最好的解决方案,所以我继续用这种方法的另一个例子来回答这个问题。
这里我需要 mock/model 一个 Setting
在代码中这样使用的(在引擎之外):my_value = Setting.find_by_name('SETTING_NAME').value
describe 'ratable_based_on_time' do
Setting = Class.new do
def self.value
end
def self.find_by_name(name)
end
end
before(:each) do
allow(Setting).to receive_message_chain(:find_by_name, :value).and_return("30")
end
let(:availability_interval) { FactoryGirl.create(:availability_interval) }
it 'should return availability_intervals that are rateable based on time since start' do
availability_interval.update_column(:start_time, 1.hour.ago)
availability_interval.reload
expect(AvailabilityInterval.ratable_based_on_time).to eq [availability_interval]
end
it 'should not return availability_intervals that are not rateable based on time since start' do
availability_interval.update_column(:start_time, 25.minutes.ago)
availability_interval.reload
expect(AvailabilityInterval.ratable_based_on_time).to eq []
end
end
我有一个 Rails 引擎,其中包含 link 某些主机应用程序 类 的方法(我知道这种类型的耦合很糟糕,但在这种情况下它是不可避免)。
我需要测试使用主机 类 的方法,但在尝试 double/mock/stub 主机 类 时出现 uninitialized constant MyEngine::BaseUser
错误(BaseUser
或 Tutor
在这种情况下)。
我曾试图通过创建模拟 类 来解决这个问题,但我认为我留下的是一个坏主意,这意味着我的测试不太有用(见下文)。
有什么想法可以做得更好,或者有更好的方向建议吗?
正如我上面所说,我是这样(糟糕地)解决这个问题的:
BaseUser = Class.new do
attr_accessor :id
def initialize(id = 1)
@id = id
end
def self.find(id)
self.new(id)
end
def tutor
Tutor.find(self.id)
end
end
class Tutor
attr_accessor :id, :first_name
def initialize(id = 1)
@id = id
@first_name = "Tutor with ID #{id}'s first name"
end
def self.find(id)
self.new(id)
end
end
it 'returns the hosts first name' do
allow(MyEngine).to receive_message_chain(:user_class, :constantize) { BaseUser }
ai = FactoryGirl.create(:availability_interval, host_id: 1)
expect(ai.host_first_name).to eq BaseUser.find(1).tutor.first_name
end
我正在测试的方法如下所示:
def host_full_name
MyEngine.user_class.constantize.find(self.host_id).tutor.full_name
end
(MyEngine.user_class
是 "BaseUser")
您的引擎将所有内容命名为您的引擎。如果您正在尝试访问实际上在基本范围内定义的 class(即在您的引擎之外),您可以强制它在带有 ::
的基本范围内找到 class,例如:
expect(ai.host_first_name).to eq ::BaseUser.find(1).tutor.first_name
我在问题中所做的似乎仍然是最好的解决方案,所以我继续用这种方法的另一个例子来回答这个问题。
这里我需要 mock/model 一个 Setting
在代码中这样使用的(在引擎之外):my_value = Setting.find_by_name('SETTING_NAME').value
describe 'ratable_based_on_time' do
Setting = Class.new do
def self.value
end
def self.find_by_name(name)
end
end
before(:each) do
allow(Setting).to receive_message_chain(:find_by_name, :value).and_return("30")
end
let(:availability_interval) { FactoryGirl.create(:availability_interval) }
it 'should return availability_intervals that are rateable based on time since start' do
availability_interval.update_column(:start_time, 1.hour.ago)
availability_interval.reload
expect(AvailabilityInterval.ratable_based_on_time).to eq [availability_interval]
end
it 'should not return availability_intervals that are not rateable based on time since start' do
availability_interval.update_column(:start_time, 25.minutes.ago)
availability_interval.reload
expect(AvailabilityInterval.ratable_based_on_time).to eq []
end
end