Ich neige dazu, vor Blöcken zu verwenden, um Instanzvariablen zu setzen. Ich verwende diese Variablen dann in meinen Beispielen. Ich bin kürzlich darauf gestoßen let()
. Laut RSpec-Dokumenten ist es gewohnt
... um eine gespeicherte Hilfsmethode zu definieren. Der Wert wird über mehrere Aufrufe im selben Beispiel zwischengespeichert, jedoch nicht über Beispiele hinweg.
Wie unterscheidet sich dies von der Verwendung von Instanzvariablen in Vorblöcken? Und wann sollten Sie let()
vs verwenden before()
?
Antworten:
Ich bevorzuge
let
aus mehreren Gründen immer eine Instanzvariable:nil
, was zu subtilen Fehlern und Fehlalarmen führen kann. Dalet
eine Methode erstellt wird, erhalten Sie eine,NameError
wenn Sie sie falsch schreiben, was ich vorzuziehen finde. Dies erleichtert auch die Überarbeitung von Spezifikationen.before(:each)
Vor jedem Beispiel wird ein Hook ausgeführt, auch wenn das Beispiel keine der im Hook definierten Instanzvariablen verwendet. Dies ist normalerweise keine große Sache, aber wenn das Einrichten der Instanzvariablen lange dauert, verschwenden Sie Zyklen. Für die durch definierte Methode wirdlet
der Initialisierungscode nur ausgeführt, wenn das Beispiel ihn aufruft.@
. B. ein hinzufügen ).let
und meinenit
Block schön kurz zu halten.Einen verwandten Link finden Sie hier: http://www.betterspecs.org/#let
quelle
let
, alle abhängigen Objekte zu definieren und diebefore(:each)
erforderliche Konfiguration oder die von den Beispielen benötigten Mocks / Stubs einzurichten. Ich ziehe dies einem großen Vorhaken vor, der all dies enthält. Auchlet(:foo) { Foo.new }
ist dann weniger laut (und mehr auf den Punkt)before(:each) { @foo = Foo.new }
. Hier ist ein Beispiel, wie ich es benutze: github.com/myronmarston/vcr/blob/v1.7.0/spec/vcr/util/…NoMethodError
bekomme ich lieber eine als eine Warnung, aber YMMV.foo = Foo.new(...)
Benutzer und dann Benutzerfoo
in späteren Zeilen hat. Später schreiben Sie ein neues Beispiel in dieselbe Beispielgruppe,Foo
die auf dieselbe Weise auch instanziiert werden muss. An dieser Stelle möchten Sie eine Umgestaltung vornehmen, um die Duplizierung zu beseitigen. Sie können diefoo = Foo.new(...)
Zeilen aus Ihren Beispielen entfernen und durch eine ersetzen, ohnelet(:foo) { Foo.new(...) }
die Verwendung der Beispiele zu ändernfoo
. Wenn Sie sich jedoch umgestalten, müssenbefore { @foo = Foo.new(...) }
Sie auch die Referenzen in den Beispielen vonfoo
bis aktualisieren@foo
.Der Unterschied zwischen den Instanzen Variablen und
let()
ist , dasslet()
ist faul ausgewertet . Dies bedeutet, dass dieslet()
erst ausgewertet wird, wenn die von ihm definierte Methode zum ersten Mal ausgeführt wird.Der Unterschied zwischen
before
undlet
besteht darin, dasslet()
Sie eine Gruppe von Variablen in einem kaskadierenden Stil definieren können. Auf diese Weise sieht die Spezifikation durch Vereinfachung des Codes etwas besser aus.quelle
let
wenn Sie jedes Mal etwas bewerten müssen? Beispielsweise muss ein untergeordnetes Modell in der Datenbank vorhanden sein, bevor ein Verhalten im übergeordneten Modell ausgelöst wird. Ich verweise im Test nicht unbedingt auf dieses untergeordnete Modell, da ich das Verhalten der übergeordneten Modelle teste. Im Moment verwende ich stattdessen dielet!
Methode, aber vielleicht wäre es expliziter, dieses Setup einzufügenbefore(:each)
?Ich habe alle Verwendungen von Instanzvariablen in meinen rspec-Tests vollständig ersetzt, um let () zu verwenden. Ich habe ein Quickie-Beispiel für einen Freund geschrieben, der damit eine kleine Rspec-Klasse unterrichtete: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html
Wie einige der anderen Antworten hier sagen, wird let () faul ausgewertet, sodass nur diejenigen geladen werden, die geladen werden müssen. Es trocknet die Spezifikation aus und macht sie lesbarer. Tatsächlich habe ich den Rspec let () -Code für die Verwendung in meinen Controllern im Stil von inherited_resource gem portiert. http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html
Neben der verzögerten Auswertung besteht der andere Vorteil darin, dass Sie in Kombination mit ActiveSupport :: Concern und der Load-Everything-In-Spezifikation / Unterstützung / Verhalten Ihre eigene Spezifikation für Ihre Anwendung erstellen können. Ich habe solche zum Testen gegen Rack- und RESTful-Ressourcen geschrieben.
Die Strategie, die ich benutze, ist Factory-Everything (über Maschinist + Fälschung / Fälscher). Es ist jedoch möglich, es in Kombination mit vor (: jedem) Blöcken zu verwenden, um Fabriken für eine ganze Reihe von Beispielgruppen vorzuladen, sodass die Spezifikationen schneller ausgeführt werden können: http://makandra.com/notes/770-taking-advantage -of-rspec-s-eingelassen-vor-Blöcken
quelle
# spec/friendship_spec.rb
und Ihr# spec/comment_spec.rb
Beispiel weniger lesbar sind? Ich habe keine Ahnung, woherusers
ich komme und muss tiefer graben.let()
die letzten paar Tage benutzt und persönlich sehe ich keinen Unterschied, außer dem ersten Vorteil, den Myron erwähnte. Und ich bin mir nicht so sicher, ob ich loslassen soll und was nicht, vielleicht weil ich faul bin und Code gerne im Voraus sehe, ohne noch eine Datei öffnen zu müssen. Danke für deine Kommentare.Es ist wichtig zu beachten, dass let faul ausgewertet wird und keine Nebenwirkungsmethoden verwendet werden, da Sie sonst nicht einfach von let zu before (: each) wechseln können. Sie können let verwenden! anstatt zu lassen, damit es vor jedem Szenario ausgewertet wird.
quelle
Im Allgemeinen
let()
handelt es sich um eine schönere Syntax, mit der Sie@name
überall Symbole eingeben können . Aber Vorbehalt Emptor! Ich habe festgestellt, dass eslet()
auch subtile Fehler (oder zumindest Kopfkratzer) gibt, da die Variable erst dann wirklich existiert, wenn Sie versuchen, sie zu verwenden ... Tell Tale-Zeichen: Wenn Sie einputs
nach dem hinzufügen, umlet()
zu sehen, ob die Variable korrekt ist, wird eine Spezifikation zugelassen zu bestehen, aber ohne dass dieputs
Spezifikation fehlschlägt - Sie haben diese Subtilität gefunden.Ich habe auch festgestellt, dass
let()
das nicht unter allen Umständen zwischenzuspeichern scheint! Ich habe es in meinem Blog geschrieben: http://technicaldebt.com/?p=1242Vielleicht bin es nur ich?
quelle
let
speichert immer den Wert für die Dauer eines einzelnen Beispiels. Der Wert wird nicht über mehrere Beispiele hinweg gespeichert.before(:all)
Im Gegensatz dazu können Sie eine initialisierte Variable in mehreren Beispielen wiederverwenden.let!
ist dies das Richtige für Sie. relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/…let ist funktional, da es im Wesentlichen ein Proc ist. Auch es ist zwischengespeichert.
Ein Gotcha, das ich sofort mit let gefunden habe ... In einem Spec-Block, der eine Änderung auswertet.
Sie müssen sicher sein, dass Sie
let
außerhalb Ihres Erwartungsblocks anrufen . dh Sie rufenFactoryGirl.create
in Ihrem Let-Block an. Normalerweise überprüfe ich, ob das Objekt erhalten bleibt.Andernfalls
let
wird beim ersten Aufruf des Blocks aufgrund der verzögerten Instanziierung tatsächlich eine Änderung in der Datenbank vorgenommen.Aktualisieren
Einfach eine Notiz hinzufügen. Seien Sie vorsichtig beim Spielen von Code Golf oder in diesem Fall Rspec Golf mit dieser Antwort.
In diesem Fall muss ich nur eine Methode aufrufen, auf die das Objekt reagiert. Also rufe ich die
_.persisted?
_-Methode für das Objekt als seine Wahrheit auf. Ich versuche nur, das Objekt zu instanziieren. Sie könnten leer anrufen? oder null? zu. Es geht nicht um den Test, sondern darum, das Objekt zum Leben zu erwecken, indem man es nennt.Sie können also nicht umgestalten
sein
da das Objekt nicht instanziiert wurde ... ist es faul. :) :)
Update 2
Nutzen Sie die Vermietung! Syntax für die sofortige Objekterstellung, die dieses Problem insgesamt vermeiden sollte. Beachten Sie jedoch, dass dies einen Großteil des Zwecks der Faulheit des nicht geknallten Let zunichte macht.
In einigen Fällen möchten Sie möglicherweise auch die Betreff-Syntax nutzen, anstatt sie zuzulassen, da Sie dadurch möglicherweise zusätzliche Optionen erhalten.
quelle
Hinweis für Joseph: Wenn Sie Datenbankobjekte in einer Datenbank erstellen, werden
before(:all)
diese nicht in einer Transaktion erfasst, und es ist viel wahrscheinlicher, dass Sie Cruft in Ihrer Testdatenbank belassen. Verwenden Siebefore(:each)
stattdessen.Der andere Grund für die Verwendung von let und seiner verzögerten Auswertung besteht darin, dass Sie ein kompliziertes Objekt nehmen und einzelne Teile testen können, indem Sie let-in-Kontexte überschreiben, wie in diesem sehr konstruierten Beispiel:
quelle
"vor" impliziert standardmäßig
before(:each)
. Siehe The Rspec Book, Copyright 2010, Seite 228.Ich verwende,
before(:each)
um einige Daten für jede Beispielgruppe zu setzen, ohne dielet
Methode aufrufen zu müssen, um die Daten im "it" -Block zu erstellen. In diesem Fall weniger Code im "it" -Block.Ich verwende,
let
wenn ich einige Daten in einigen Beispielen möchte, andere jedoch nicht.Sowohl vorher als auch lassen sind großartig, um die "es" -Blöcke aufzutrocknen.
Um Verwirrung zu vermeiden, ist "let" nicht dasselbe wie
before(:all)
. "Let" bewertet seine Methode und seinen Wert für jedes Beispiel neu ("it"), speichert den Wert jedoch zwischen mehreren Aufrufen im selben Beispiel zwischen. Weitere Informationen finden Sie hier: https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-letquelle
Gegenstimme hier: Nach 5 Jahren rspec mag ich nicht
let
sehr.1. Eine träge Auswertung macht den Testaufbau oft verwirrend
Es wird schwierig, über das Setup nachzudenken, wenn einige Dinge, die im Setup deklariert wurden, den Status nicht tatsächlich beeinflussen, während andere dies tun.
Irgendwann wechselt jemand aus Frustration einfach
let
zulet!
(dasselbe ohne faule Bewertung), um seine Spezifikation zum Laufen zu bringen. Wenn dies für sich arbeitet, wird eine neue Gewohnheit geboren: wenn eine neue Spezifikation zu einer älteren Suite hinzugefügt wird und es nicht funktioniert, die erste Sache , versucht der Schriftsteller Pony zufällig hinzufügenlet
Anrufe.Ziemlich bald sind alle Leistungsvorteile weg.
2. Spezielle Syntax ist für Nicht-Rspec-Benutzer ungewöhnlich
Ich würde meinem Team lieber Ruby beibringen als die Tricks von rspec. Instanzvariablen oder Methodenaufrufe sind überall in diesem und anderen Projekten
let
nützlich. Die Syntax ist nur in rspec nützlich.3. Die "Vorteile" ermöglichen es uns, gute Designänderungen leicht zu ignorieren
let()
ist gut für teure Abhängigkeiten, die wir nicht immer wieder erstellen möchten. Es passt auch gut zusubject
, sodass Sie wiederholte Aufrufe von Methoden mit mehreren Argumenten austrocknen könnenTeure Abhängigkeiten, die oft wiederholt werden, und Methoden mit großen Signaturen sind beide Punkte, an denen wir den Code verbessern könnten:
In all diesen Fällen kann ich das Symptom schwieriger Tests mit einem beruhigenden Balsam aus Rspec-Magie behandeln oder versuchen, die Ursache zu beheben. Ich habe das Gefühl, dass ich in den letzten Jahren viel zu viel mit dem ersteren verbracht habe und jetzt möchte ich einen besseren Code.
Um die ursprüngliche Frage zu beantworten: Ich würde es lieber nicht tun, aber ich benutze es immer noch
let
. Ich benutze es meistens, um mich dem Stil des restlichen Teams anzupassen (es scheint, als ob die meisten Rails-Programmierer auf der Welt jetzt tief in ihrer Rspec-Magie stecken, so dass dies sehr oft der Fall ist). Manchmal verwende ich es, wenn ich einem Code, über den ich keine Kontrolle habe, einen Test hinzufüge oder keine Zeit habe, eine bessere Abstraktion vorzunehmen: dh wenn die einzige Option das Schmerzmittel ist.quelle
Ich
let
teste meine HTTP 404-Antworten in meinen API-Spezifikationen mithilfe von Kontexten.Um die Ressource zu erstellen, verwende ich
let!
. Aber um die Ressourcenkennung zu speichern, verwende ichlet
. Schauen Sie sich an, wie es aussieht:Das hält die Spezifikationen sauber und lesbar.
quelle