我对遗留项目有这种情况。假设我有rails模型:
rails g model entity name:string
在该模型中有MAIN_ENTITY
常数:
class Entity < ActiveRecord::Base
MAIN_ENTITY_ID = 1
MAIN_ENTITY = Entity.find(MAIN_ENTITY_ID)
end
现在我要用rspec测试覆盖Entity
- 模型,但首先我需要工厂。我创建了这个工厂:
FactoryGirl.define do
factory :entity do
name { FFaker::Company.name }
end
end
当我在rspec测试中运行create(:entity)
时,我收到此错误:
Failure/Error: MAIN_ENTITY = Entity.find(MAIN_ENTITY_ID)
ActiveRecord::RecordNotFound:
Couldn't find Entity with 'id'=1
如何在不重构代码并保持MAIN_ENTITY
不变的情况下解决这个问题?
答案 0 :(得分:3)
在访问类的静态上下文之前初始化Ruby常量。因此,当您调用Entity
类(手动或使用FactoryGirl)时,在后台有SQL查询,它正在尝试使用id == 1
查找记录。
在生产的情况下,此代码正常运行,但是当您编写测试时,您的数据库通常会在每个测试用例运行后清理(rspec&#39; s it
)。所以解决方案是创建主要实体&#39;在每个测试用例在空数据库中运行之前:
# Your DatabaseCleaner initializer file
RSpec.configure do |config|
# ...
config.around(:each) do |example|
DatabaseCleaner.cleaning do
FactoryGirl.create_main_entity
example.run
end
end
end
在create_main_entity
方法中我们有限制:我们无法使用Entity.create
或Entity.new
方法,因此我们应该使用更低级别的解决方案:我们需要进行INSERT
查询直接进入数据库。我会像这样重写你的entities.rb
- 工厂:
FactoryGirl.define do
factory :entity do
name { FFaker::Company.name }
end
end
module FactoryGirl
def self.create_main_entity
connection = ActiveRecord::Base.connection
values = {
id: 1,
name: 'MAIN ENTITY',
created_at: Time.now.to_s(:db),
updated_at: Time.now.to_s(:db)
}
sql = <<-SQL
INSERT INTO entities (#{values.keys.map {|k| k.to_s}.join(', ')})
VALUES(#{values.values.map{"'%s'"}.join(', ')})
SQL
connection.insert(sql % values.values)
end
end
上面的代码是临时解决方案。我建议你重构你的Entity
模型。不是通过查找记录来初始化常量,而是可以使用静态方法:
class Entity < ActiveRecord::Base
MAIN_ENTITY_ID = 1
def self.main_entity
@@main_entity ||= Entity.find(MAIN_ENTITY_ID)
end
end
我在@@main_entity ||= ...
使用了memorization,以避免在每个Entity.main_entity
调用上执行相同的SELECT查询。