Ruby-on-rails 工厂女孩创建绕过我的模型验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9322353/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 02:50:52  来源:igfitidea点击:

Factory-girl create that bypasses my model validation

ruby-on-railsruby-on-rails-3rspecfactory-botrspec-rails

提问by Norto23

I am using Factory Girl to create two instances in my model/unit test for a Group. I am testing the model to check that a call to .current returns only the 'current' groups according to the expiry attribute as per below...

我正在使用 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

My problem is that I've got validation in the model that checks a new group's expiry is after today's date. This raises the validation failure below.

我的问题是我在模型中得到了验证,该模型检查新组的到期时间是在今天的日期之后。这会引发下面的验证失败。

  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

Is there a way to forcefully create the Group or get around the validation when creating using Factory Girl?

有没有办法在使用 Factory Girl 创建时强制创建组或绕过验证?

回答by Brandan

This isn't very specific to FactoryGirl, but you can always bypass validations when saving models via save(:validate => false):

这不是 FactoryGirl 特有的,但是在通过save(:validate => false)以下方式保存模型时,您始终可以绕过验证:

describe ".current" do
  let!(:current_group) { FactoryGirl.create(:group) }
  let!(:old_group) {
    g = FactoryGirl.build(:group, :expiry => Time.now - 3.days)
    g.save(:validate => false)
    g
  }

  specify { Group.current.should == [current_group] }
end

回答by Jason Denney

I prefer this solution from https://github.com/thoughtbot/factory_girl/issues/578.

我更喜欢https://github.com/thoughtbot/factory_girl/issues/578 中的这个解决方案。

Inside the factory:

工厂内部:

to_create {|instance| instance.save(validate: false) }

EDIT:

编辑:

As mentioned in the referenced thread, and by other's comments/solutions, you'll likely want to wrap this in a trait block to avoid confusion/issues elsewhere in your tests; for example, when you're testing your validations.

正如在引用的线程中以及其他人的评论/解决方案中所提到的,您可能希望将其包装在一个 trait 块中,以避免在您的测试中的其他地方出现混淆/问题;例如,当您测试验证时。

回答by Tim Scott

It's a bad idea to skip validations by default in factory. Some hair will be pulled out finding that.

默认情况下在工厂中跳过验证是一个坏主意。发现会拔掉一些头发。

The nicest way, I think:

最好的方法,我认为:

trait :skip_validate do
  to_create {|instance| instance.save(validate: false)}
end

Then in your test:

然后在你的测试中:

create(:group, :skip_validate, expiry: Time.now + 1.week)

回答by Chris Habgood

foo = build(:foo).tap{ |u| u.save(validate: false) }

回答by Gabe Martin-Dempesy

For this specific date-baesd validation case, you could also use the timecopgem to temporarily alter time to simulate the old record being created in the past.

对于这个特定的基于日期的验证案例,您还可以使用timecopgem 临时更改时间以模拟过去创建的旧记录。

回答by HAZI

It is not best to skip all validation of that model.

最好不要跳过对该模型的所有验证。

create spec/factories/traits.rbfile.

创建spec/factories/traits.rb文件。

FactoryBot.define do
  trait :skip_validate do
    to_create { |instance| instance.save(validate: false) }
  end
end

fix spec

修正规格

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

回答by acamino

Your factories should create valid objects by default. I found that transient attributescan be used to add conditional logic like this:

默认情况下,您的工厂应该创建有效的对象。我发现瞬态属性可用于添加这样的条件逻辑:

transient do
  skip_validations false
end

before :create do |instance, evaluator|
  instance.save(validate: false) if evaluator.skip_validations
end

In your test:

在您的测试中:

create(:group, skip_validations: true)

回答by JoaoHornburg

Depending on your scenario you could change validation to happen only on update. Example: :validates :expire_date, :presence => true, :on => [:update ]

根据您的情况,您可以将验证更改为仅在更新时发生。例子::validates :expire_date, :presence => true, :on => [:update ]

回答by brcebn

Or you can use both FactoryBotand Timecopwith something like:

或者您可以同时使用FactoryBotTimecop,例如:

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) }

Edit: Do not update this event after creation or validations will fail.

编辑:创建后不要更新此事件,否则验证将失败。