绕过我的模型验证的工厂女孩创建
Posted
技术标签:
【中文标题】绕过我的模型验证的工厂女孩创建【英文标题】:Factory-girl create that bypasses my model validation 【发布时间】:2012-03-08 12:06:48 【问题描述】:我正在使用 Factory Girl 在我的模型/单元测试中为一个组创建两个实例。我正在测试模型以检查对 .current 的调用是否仅根据到期属性返回“当前”组,如下所示...
describe ".current" do
let!(:current_group) FactoryGirl.create(:group, :expiry => Time.now + 1.week)
let!(:expired_group) FactoryGirl.create(:group, :expiry => Time.now - 3.days)
specify Group.current.should == [current_group]
end
我的问题是我在模型中得到验证,检查新组的到期时间是在今天之后。这会引发下面的验证失败。
1) Group.current
Failure/Error: let!(:expired_group) FactoryGirl.create(:group, :expiry => Time.now - 3.days)
ActiveRecord::RecordInvalid:
Validation failed: Expiry is before todays date
在使用 Factory Girl 创建时,有没有办法强制创建组或绕过验证?
【问题讨论】:
【参考方案1】:这并不是 FactoryGirl 特有的,但您在通过 save(validate: false)
保存模型时始终可以绕过验证:
describe ".current" do
let!(:current_group) FactoryGirl.create(:group)
let!(:old_group) do
g = FactoryGirl.build(:group, expiry: Time.now - 3.days)
g.save(validate: false)
g
end
specify Group.current.should == [current_group]
end
【讨论】:
查看下面 Jason Denney 的回答以获得更好的解决方案。 从 1.9.1 开始你可以做g.tap |g| g.save(validate: false)
【参考方案2】:
我更喜欢https://github.com/thoughtbot/factory_girl/issues/578的这个解决方案。
工厂内部:
trait :without_validations do
to_create |instance| instance.save(validate: false)
end
【讨论】:
这是一个比公认的更优雅的解决方案。 请记住,如果您为通用工厂执行此操作,那么您每次在该工厂创建时都会跳过验证。最好只在子工厂(或特征)上使用这种技术。 你几乎肯定会想把它放在一个特征中。请参阅下面 Tim Scott 的答案。【参考方案3】:在工厂默认跳过验证是个坏主意。发现这一点会拔掉一些头发。
我认为最好的方法:
trait :skip_validate do
to_create |instance| instance.save(validate: false)
end
然后在你的测试中:
create(:group, :skip_validate, expiry: Time.now + 1.week)
【讨论】:
有没有办法将此应用于所有工厂?【参考方案4】:对于这个特定的基于日期的验证案例,您还可以使用timecop gem 临时更改时间以模拟过去创建的旧记录。
【讨论】:
【参考方案5】:foo = build(:foo).tap |u| u.save(validate: false)
【讨论】:
【参考方案6】:最好不要跳过对该模型的所有验证。
创建spec/factories/traits.rb
文件。
FactoryBot.define do
trait :skip_validate do
to_create |instance| instance.save(validate: false)
end
end
修复规范
describe ".current" do
let!(:current_group) FactoryGirl.create(:group, :skip_validate, :expiry => Time.now + 1.week)
let!(:expired_group) FactoryGirl.create(:group, :skip_validate, :expiry => Time.now - 3.days)
specify Group.current.should == [current_group]
end
【讨论】:
【参考方案7】:默认情况下,您的工厂应该创建有效的对象。我发现transient attributes可以用来添加这样的条件逻辑:
transient do
skip_validations false
end
before :create do |instance, evaluator|
instance.save(validate: false) if evaluator.skip_validations
end
在你的测试中:
create(:group, skip_validations: true)
【讨论】:
【参考方案8】:根据您的情况,您可以将验证更改为仅在更新时发生。示例::validates :expire_date, :presence => true, :on => [:update ]
【讨论】:
【参考方案9】:或者您可以同时使用FactoryBot
和Timecop
,例如:
trait :expired do
transient do
travel_backward_to 2.days.ago
end
before(:create) do |_instance, evaluator|
Timecop.travel(evaluator.travel_backward_to)
end
after(:create) do
Timecop.return
end
end
let!(:expired_group) FactoryGirl.create(:group, :expired, travel_backward_to: 5.days.ago, expiry: Time.now - 3.days)
编辑:创建后不要更新此事件,否则验证将失败。
【讨论】:
以上是关于绕过我的模型验证的工厂女孩创建的主要内容,如果未能解决你的问题,请参考以下文章
每次使用 YouTube Data API v3 时如何绕过输入验证码来授权我的代码