Factory-Girl erstellen, das meine Modellvalidierung umgeht


77

Ich verwende Factory Girl, um zwei Instanzen in meinem Modell- / Komponententest für eine Gruppe zu erstellen. Ich teste das Modell, um zu überprüfen, ob ein Aufruf von .current nur die 'aktuellen' Gruppen gemäß dem unten angegebenen Ablaufattribut zurückgibt ...

  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

Mein Problem ist, dass ich eine Validierung im Modell habe, die überprüft, ob der Ablauf einer neuen Gruppe nach dem heutigen Datum liegt. Dies löst den folgenden Validierungsfehler aus.

  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

Gibt es eine Möglichkeit, die Gruppe zwangsweise zu erstellen oder die Validierung zu umgehen, wenn Sie mit Factory Girl erstellen?

Antworten:


91

Dies ist nicht sehr spezifisch für FactoryGirl, aber Sie können die Validierungen beim Speichern von Modellen jederzeit umgehen über 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

Eine bessere Lösung finden Sie in der Antwort von Jason Denney unten.
David Hempy

1
seit 1.9.1 können Sie tung.tap { |g| g.save(validate: false) }
yefrem

58

Ich bevorzuge diese Lösung von https://github.com/thoughtbot/factory_girl/issues/578 .

In der Fabrik:

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

BEARBEITEN:

Wie im Thread, auf den verwiesen wird, und in den Kommentaren / Lösungen anderer erwähnt, möchten Sie dies wahrscheinlich in einen Merkmalsblock einschließen, um Verwirrung / Probleme an anderer Stelle in Ihren Tests zu vermeiden. Zum Beispiel, wenn Sie Ihre Validierungen testen.


5
Dies ist eine viel elegantere Lösung als die akzeptierte.
Kyle Heironimus

5
Denken Sie daran, dass Sie, wenn Sie dies für Ihre Allzweckfabrik tun, JEDES Mal, wenn Sie diese Fabrik erstellt haben, Validierungen überspringen. Es ist wahrscheinlich am besten, diese Technik nur in einer Unterfabrik (oder in einem Merkmal) anzuwenden.
tgf

Sie werden dies mit ziemlicher Sicherheit in eine Eigenschaft umwandeln wollen. Siehe die Antwort von Tim Scott unten.
David Hempy

40

Es ist eine schlechte Idee, Validierungen standardmäßig im Werk zu überspringen. Einige Haare werden herausgezogen, um das zu finden.

Der schönste Weg, denke ich:

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

Dann in Ihrem Test:

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

1
Dies ist der beste Weg, um dieses Problem zu lösen!
Hatenine

Gibt es eine Möglichkeit, dies auf alle Fabriken anzuwenden?
Adam

Es ist immer wieder erstaunlich, diese kleinen Schnipsel in bereits vorhandenen Codebasen zu finden. Sie wissen, dass ein aktueller oder ehemaliger Mitarbeiter diese genaue Antwort bereits gefunden hat.
Dylan Pierce



6

Es ist nicht am besten, die gesamte Validierung dieses Modells zu überspringen.

spec/factories/traits.rbDatei erstellen .

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

1

Abhängig von Ihrem Szenario können Sie die Validierung so ändern, dass sie nur beim Update erfolgt. Beispiel::validates :expire_date, :presence => true, :on => [:update ]


1

Ihre Fabriken sollten standardmäßig gültige Objekte erstellen. Ich fand heraus, dass transiente Attribute verwendet werden können, um bedingte Logik wie folgt hinzuzufügen:

transient do
  skip_validations false
end

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

In Ihrem Test:

create(:group, skip_validations: true)

0

Oder Sie können beide FactoryBotund Timecopmit so etwas wie:

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

Bearbeiten : Aktualisieren Sie dieses Ereignis nicht, nachdem die Erstellung oder Validierung fehlgeschlagen ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.