Zusammenfassung: Das Subjekt von RSpec ist eine spezielle Variable, die sich auf das zu testende Objekt bezieht. Es können implizit Erwartungen gesetzt werden, was einzeilige Beispiele unterstützt. Es ist dem Leser in einigen idiomatischen Fällen klar, aber ansonsten schwer zu verstehen und sollte vermieden werden. Die let
Variablen von RSpec sind nur träge instanziierte (gespeicherte) Variablen. Sie sind nicht so schwer zu verfolgen wie das Thema, können aber dennoch zu verworrenen Tests führen und sollten daher mit Diskretion verwendet werden.
Das Thema
Wie es funktioniert
Das Subjekt ist das zu testende Objekt. RSpec hat eine explizite Vorstellung von dem Thema. Es kann definiert sein oder nicht. Wenn dies der Fall ist, kann RSpec Methoden aufrufen, ohne explizit darauf zu verweisen.
Wenn das erste Argument für eine äußerste Beispielgruppe ( describe
oder einen äußersten context
Block) eine Klasse ist, erstellt RSpec standardmäßig eine Instanz dieser Klasse und weist sie dem Betreff zu. Zum Beispiel passieren die folgenden:
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
Sie können das Thema selbst definieren mit subject
:
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Sie können dem Thema einen Namen geben, wenn Sie es definieren:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
Auch wenn Sie das Thema benennen, können Sie anonym darauf verweisen:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Sie können mehr als ein benanntes Thema definieren. Das zuletzt definierte benannte Thema ist das anonyme subject
.
Das Thema ist jedoch definiert,
Es ist träge instanziiert. Das heißt, die implizite Instanziierung der beschriebenen Klasse oder die Ausführung des an übergebenen Blocks subject
erfolgt erst, wenn subject
in einem Beispiel auf das benannte Subjekt verwiesen wird. Wenn Sie möchten, dass Ihr explizites Thema eifrig instanziiert wird (bevor ein Beispiel in seiner Gruppe ausgeführt wird), sagen Sie subject!
statt subject
.
Erwartungen können implizit darauf gesetzt werden (ohne zu schreiben subject
oder den Namen eines benannten Subjekts):
describe A do
it { is_expected.to be_an(A) }
end
Der Betreff unterstützt diese einzeilige Syntax.
Wann man es benutzt
Ein implizites subject
(aus der Beispielgruppe abgeleitet) ist schwer zu verstehen, weil
- Es wird hinter den Kulissen instanziiert.
- Unabhängig davon, ob es implizit (durch Aufrufen
is_expected
ohne expliziten Empfänger) oder explizit (as subject
) verwendet wird, gibt es dem Leser keine Informationen über die Rolle oder die Art des Objekts, für das die Erwartung aufgerufen wird.
- Die einzeilige Beispielsyntax enthält keine Beispielbeschreibung (das Zeichenfolgenargument
it
in der normalen Beispielsyntax). Die einzige Information, die der Leser über den Zweck des Beispiels hat, ist die Erwartung selbst.
Daher ist es nur dann hilfreich, ein implizites Thema zu verwenden, wenn der Kontext wahrscheinlich von allen Lesern gut verstanden wird und eine Beispielbeschreibung wirklich nicht erforderlich ist . Der kanonische Fall ist das Testen von ActiveRecord-Validierungen mit Shoulda-Matchern:
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
Eine explizite Erklärung subject
(definiert subject
ohne Namen) ist etwas besser, da der Leser sehen kann, wie sie instanziiert wird, aber
- Es kann die Instanziierung des Themas immer noch weit von dem Ort entfernen, an dem es verwendet wird (z. B. am Anfang einer Beispielgruppe mit vielen Beispielen, die es verwenden), was immer noch schwer zu verfolgen ist
- es hat die anderen Probleme, die das implizite Subjekt hat.
Ein benannter Betreff enthält einen absichtsvollen Namen. Der einzige Grund, einen benannten Betreff anstelle einer let
Variablen zu verwenden, besteht darin, dass Sie den anonymen Betreff manchmal verwenden möchten. Wir haben nur erklärt, warum der anonyme Betreff schwer zu verstehen ist.
Daher sind legitime Verwendungen eines expliziten anonymen subject
oder benannten Themas sehr selten .
let
Variablen
Wie sie arbeiten
let
Variablen sind bis auf zwei Unterschiede genau wie benannte Subjekte:
- Sie werden mit
let
/ let!
anstelle von subject
/ definiertsubject!
- Sie setzen das Anonyme nicht
subject
und lassen nicht zu, dass Erwartungen implizit darauf bezogen werden.
Wann man sie benutzt
Es ist völlig legitim, let
Doppelarbeit zwischen Beispielen zu reduzieren. Tun Sie dies jedoch nur, wenn die Testklarheit nicht beeinträchtigt wird. Die sicherste Zeit let
ist, wenn der let
Zweck der Variablen vollständig aus ihrem Namen hervorgeht (damit der Leser nicht die Definition finden muss, die viele Zeilen entfernt sein kann, um jedes Beispiel zu verstehen) und sie auf die gleiche Weise verwendet wird in jedem Beispiel. Wenn eines dieser Dinge nicht zutrifft, können Sie das Objekt in einer einfachen alten lokalen Variablen definieren oder direkt im Beispiel eine Factory-Methode aufrufen.
let!
ist riskant, weil es nicht faul ist. Wenn jemand der Beispielgruppe, die das enthält let!
, ein Beispiel hinzufügt , das Beispiel jedoch die let!
Variable nicht benötigt ,
- Dieses Beispiel ist schwer zu verstehen, da der Leser die
let!
Variable sieht und sich fragt, ob und wie sie sich auf das Beispiel auswirkt
- Das Beispiel ist aufgrund der Zeit, die zum Erstellen der
let!
Variablen benötigt wird, langsamer als erforderlich
Verwenden Sie diese also let!
, wenn überhaupt, nur in kleinen, einfachen Beispielgruppen, in denen es weniger wahrscheinlich ist, dass zukünftige Beispielautoren in diese Falle tappen.
Der Single-Expectation-per-Example-Fetisch
Es gibt eine häufige Überbeanspruchung von Themen oder let
Variablen, die es wert ist, separat diskutiert zu werden. Einige Leute benutzen sie gerne so:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(Dies ist ein einfaches Beispiel für eine Methode, die eine Zahl zurückgibt, für die wir zwei Erwartungen benötigen. Dieser Stil kann jedoch viel mehr Beispiele / Erwartungen enthalten, wenn die Methode einen komplizierteren Wert zurückgibt, der viele Erwartungen erfordert und / oder viele Nebenwirkungen hat Alle brauchen Erwartungen.)
Die Leute tun dies, weil sie gehört haben, dass man nur eine Erwartung pro Beispiel haben sollte (was mit der gültigen Regel verwechselt wird, dass man nur einen Methodenaufruf pro Beispiel testen sollte) oder weil sie in RSpec-Tricks verliebt sind. Tun Sie es nicht, ob mit einem anonymen oder benannten Betreff oder einer let
Variablen! Dieser Stil hat mehrere Probleme:
- Das anonyme Thema ist nicht das Thema der Beispiele - die Methode ist das Thema. Wenn Sie den Test auf diese Weise schreiben, wird die Sprache durcheinander gebracht, was das Nachdenken erschwert.
- Wie immer bei einzeiligen Beispielen gibt es keinen Raum, um die Bedeutung der Erwartungen zu erklären.
- Das Thema muss für jedes Beispiel konstruiert werden, was langsam ist.
Schreiben Sie stattdessen ein einzelnes Beispiel:
describe 'Calculator' do
describe '#calculate' do
it "returns a single-digit number" do
result = Calculator.calculate
expect(result).to be >= 0
expect(result).to be <= 9
end
end
end
:aggregate_failures
Tag auch in einer Zeile wieit "marks a task complete", :aggregate_failures do
(aus dem Buch Rails 5 Test Prescriptions)expect(result).to be_between(0, 9)
.expect(result.to_s).to match(/^[0-9]$/)
- ich weiß, dass es hässlich ist, aber es testet wirklich, was Sie sagen, oder verwenden Sie vielleichtbetween
+is_a? Integer
, aber hier testen Sie der Typ auch. Und nurlet
... es sollte kein Gegenstand von Bedenken sein, und es kann tatsächlich besser sein, Werte zwischen Beispielen neu zu bewerten. Ansonsten +1 für die Postlet!
und war es nicht, meine Teamkollegen alleine zu überzeugen. Ich werde diese Antwort senden.Subject
undlet
sind nur Werkzeuge, mit denen Sie Ihre Tests aufräumen und beschleunigen können. Die Leute in der rspec-Community verwenden sie, damit ich mir keine Sorgen machen kann, ob es in Ordnung ist, sie zu verwenden oder nicht. Sie können ähnlich verwendet werden, dienen jedoch leicht unterschiedlichen ZweckenSubject
Mit dieser Option können Sie eine Testperson deklarieren und anschließend für eine beliebige Anzahl folgender Testfälle wiederverwenden. Dies reduziert die Code-Wiederholung (TROCKNEN Ihres Codes)Let
ist eine Alternative zubefore: each
Blöcken, die Instanzvariablen Testdaten zuweisen.Let
gibt Ihnen ein paar Vorteile. Zunächst wird der Wert zwischengespeichert, ohne ihn einer Instanzvariablen zuzuweisen. Zweitens wird es träge ausgewertet, was bedeutet, dass es erst ausgewertet wird, wenn eine Spezifikation es erfordert. So könnenlet
Sie Ihre Tests beschleunigen. Ich denke auch, dasslet
es leichter zu lesen istquelle
subject
ist das, was getestet wird, normalerweise eine Instanz oder eine Klasse.let
dient zum Zuweisen von Variablen in Ihren Tests, die im Vergleich zur Verwendung von Instanzvariablen träge ausgewertet werden. Es gibt einige schöne Beispiele in diesem Thread.https://github.com/reachlocal/rspec-style-guide/issues/6
quelle