Question

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...

  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?

Was it helpful?

Solution

This isn't very specific to FactoryGirl, but you can always bypass validations when saving models via 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

OTHER TIPS

I prefer this solution from 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.

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)

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

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

Your factories should create valid objects by default. I found that transient attributes can 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)

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

create spec/factories/traits.rb file.

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

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top