Ich teste ein Modell mit einem After-Create-Rückruf, den ich beim Testen nur gelegentlich ausführen möchte. Wie kann ich Rückrufe von einer Fabrik aus überspringen / ausführen?
class User < ActiveRecord::Base
after_create :run_something
...
end
Fabrik:
FactoryGirl.define do
factory :user do
first_name "Luiz"
last_name "Branco"
...
# skip callback
factory :with_run_something do
# run callback
end
end
ruby-on-rails
rspec
factory-bot
luizbranco
quelle
quelle
:on => :create
Validierung überspringen möchten , verwenden Sieafter(:build) { |user| user.class.skip_callback(:validate, :create, :after, :run_something) }
Class.skip_callback
bleibt der Anruf bei anderen Tests bestehen. Wenn Ihre anderen Tests also den Rückruf erwarten, schlagen sie fehl, wenn Sie versuchen, die überspringende Rückruflogik zu invertieren.after(:build)
Block verwendet. Dadurch kann Ihre Werkseinstellung den Rückruf standardmäßig ausführen und muss den Rückruf nicht nach jeder Verwendung zurücksetzen.Wenn Sie keinen Rückruf ausführen möchten, gehen Sie wie folgt vor:
Beachten Sie, dass skip_callback nach der Ausführung über andere Spezifikationen hinweg bestehen bleibt. Beachten Sie daher Folgendes:
quelle
Keine dieser Lösungen ist gut. Sie verunstalten die Klasse, indem sie Funktionen entfernen, die aus der Instanz und nicht aus der Klasse entfernt werden sollen.
Anstatt den Rückruf zu unterdrücken, unterdrücke ich die Funktionalität des Rückrufs. In gewisser Weise gefällt mir dieser Ansatz besser, weil er expliziter ist.
quelle
around_*
(zuser.define_singleton_method(:around_callback_method){|&b| b.call }
. B. ).Ich möchte die Antwort von @luizbranco verbessern, um den Rückruf nach dem Speichern beim Erstellen anderer Benutzer wiederverwendbarer zu machen.
Laufen ohne after_save-Rückruf:
Ausführen mit after_save-Rückruf:
In meinem Test ziehe ich es vor, Benutzer standardmäßig ohne Rückruf zu erstellen, da die verwendeten Methoden zusätzliche Dinge ausführen, die ich normalerweise in meinen Testbeispielen nicht möchte.
---------- UPDATE ------------ Ich habe die Verwendung von skip_callback eingestellt, da in der Testsuite einige Inkonsistenzprobleme aufgetreten sind.
Alternative Lösung 1 (Verwendung von Stub und Unstub):
Alternative Lösung 2 (mein bevorzugter Ansatz):
quelle
Rails 5 -
skip_callback
Auslösen eines Argumentfehlers beim Überspringen von einer FactoryBot-Factory.Es gab eine Änderung in Rails 5 mit wie skip_callback Griffe unerkannt Rückrufe:
Wenn
skip_callback
ab Werk aufgerufen wird, ist der tatsächliche Rückruf im AR-Modell noch nicht definiert.Wenn Sie alles ausprobiert und sich wie ich die Haare ausgezogen haben, finden Sie hier Ihre Lösung (aus der Suche nach FactoryBot-Problemen) ( HINWEIS zum
raise: false
Teil ):Fühlen Sie sich frei, es mit anderen Strategien zu verwenden, die Sie bevorzugen.
quelle
Diese Lösung funktioniert für mich und Sie müssen Ihrer Factory-Definition keinen zusätzlichen Block hinzufügen:
quelle
Ein einfacher Stub hat in Rspec 3 am besten für mich funktioniert
quelle
User
;:run_something
ist keine Klassenmethode.Wichtiger Hinweis, Sie sollten beide angeben. Wenn Sie nur vorher verwenden und mehrere Spezifikationen ausführen, wird versucht, den Rückruf mehrmals zu deaktivieren. Es wird beim ersten Mal erfolgreich sein, aber beim zweiten Mal wird kein Rückruf mehr definiert. Also wird es einen Fehler geben
quelle
Das Aufrufen von skip_callback aus meiner Fabrik erwies sich für mich als problematisch.
In meinem Fall habe ich eine Dokumentklasse mit einigen s3-bezogenen Rückrufen vor und nach dem Erstellen, die ich nur ausführen möchte, wenn das Testen des vollständigen Stapels erforderlich ist. Ansonsten möchte ich diese s3-Rückrufe überspringen.
Als ich in meiner Factory übersprang_callbacks versuchte, blieb dieser Rückrufsprung bestehen, selbst wenn ich ein Dokumentobjekt direkt ohne Verwendung einer Factory erstellt habe. Also habe ich stattdessen Mokka-Stubs im After-Build-Aufruf verwendet und alles funktioniert perfekt:
quelle
before_validation
Haken arbeitet (versucht,skip_callback
mit einer der FactoryGirlsbefore
oderafter
Optionen für zu arbeitenbuild
undcreate
hat nicht funktioniert)Dies funktioniert mit der aktuellen rspec-Syntax (ab diesem Beitrag) und ist viel sauberer:
quelle
Die Antwort von James Chevalier, wie man den Rückruf vor_validierung überspringt, hat mir nicht geholfen. Wenn Sie also genauso wie ich zappeln, ist dies eine funktionierende Lösung:
im Modell:
in der Fabrik:
quelle
Model.skip_callback(...)
In meinem Fall lädt der Rückruf etwas in meinen Redis-Cache. Aber dann hatte / wollte ich keine Redis-Instanz für meine Testumgebung.
Für meine Situation, ähnlich wie oben, habe ich meine
load_to_cache
Methode in meinem spec_helper mit folgenden Schritten gestoppt:In bestimmten Situationen, in denen ich dies testen möchte, muss ich sie nur im Vorher-Block der entsprechenden Rspec-Testfälle entfernen.
Ich weiß, dass in Ihnen möglicherweise etwas Komplizierteres passiert
after_create
oder dass Sie dies nicht sehr elegant finden. Sie können versuchen, den in Ihrem Modell definierten Rückruf abzubrechen, indem Sieafter_create
in Ihrer Factory einen Hook definieren (siehe factory_girl-Dokumente), in dem Sie wahrscheinlich denselben Rückruf definieren und zurückgeben könnenfalse
, wie im Abschnitt "Rückrufe abbrechen" dieses Artikels beschrieben . (Ich bin mir nicht sicher, in welcher Reihenfolge der Rückruf ausgeführt wird, weshalb ich mich nicht für diese Option entschieden habe.)Schließlich (Entschuldigung, ich kann den Artikel nicht finden) können Sie mit Ruby eine schmutzige Metaprogrammierung verwenden, um einen Rückruf-Hook zu lösen (Sie müssen ihn zurücksetzen). Ich denke, dies wäre die am wenigsten bevorzugte Option.
Nun, es gibt noch eine Sache, nicht wirklich eine Lösung, aber sehen Sie, ob Sie mit Factory.build in Ihren Spezifikationen davonkommen können, anstatt das Objekt tatsächlich zu erstellen. (Wäre das einfachste wenn du kannst).
quelle
In Bezug auf die oben angegebene Antwort https://stackoverflow.com/a/35562805/2001785 müssen Sie den Code nicht zur Fabrik hinzufügen. Ich fand es einfacher, die Methoden in den Spezifikationen selbst zu überladen. Zum Beispiel anstelle von (in Verbindung mit dem Fabrikcode im zitierten Beitrag)
Ich benutze gerne (ohne den angegebenen Fabrikcode)
Auf diese Weise müssen Sie nicht sowohl die Werks- als auch die Testdateien betrachten, um das Verhalten des Tests zu verstehen.
quelle
Ich fand die folgende Lösung sauberer, da der Rückruf auf Klassenebene ausgeführt / festgelegt wird.
quelle
Hier ist ein Ausschnitt, den ich erstellt habe, um dies generisch zu handhaben.
Es wird jeder konfigurierte Rückruf übersprungen, einschließlich railbezogener Rückrufe wie
before_save_collection_association
, aber es werden nicht einige übersprungen, die erforderlich sind, damit ActiveRecord funktioniert, wie automatisch generierteautosave_associated_records_for_
Rückrufe.dann später:
Unnötig zu sagen, YMMV, schauen Sie also in den Testprotokollen nach, was Sie wirklich überspringen. Vielleicht haben Sie einen Edelstein, der einen Rückruf hinzufügt, den Sie wirklich brauchen, und der dazu führt, dass Ihre Tests kläglich scheitern, oder von Ihrem Fettmodell mit 100 Rückrufen benötigen Sie nur ein Paar für einen bestimmten Test. Versuchen Sie in diesen Fällen den Transienten
:force_callbacks
BONUS
Manchmal müssen Sie auch Validierungen überspringen (um Tests schneller zu machen) und dann versuchen:
quelle
Sie können den Rückruf einfach mit einem Merkmal für diese Instanzen festlegen, wenn Sie ihn ausführen möchten.
quelle