http://betterspecs.org/#subject contient des informations sur subject
et let
. Cependant, je ne suis toujours pas clair sur la différence entre eux. De plus, le SO post Quel est l’argument contre l’utilisation de before, let et subject dans les tests RSpec? dit qu’il est préférable de ne pas utiliser ni subject
ou let
Où devrais-je aller? Je suis tellement confus.
Résumé: le sujet de RSpec est une variable spéciale qui fait référence à l'objet testé. Les attentes peuvent être définies de manière implicite, ce qui prend en charge des exemples d'une ligne. Cela est clair pour le lecteur dans certains cas idiomatiques, mais il est par ailleurs difficile à comprendre et doit être évité. Les variables let
de RSpec ne sont que des variables instanciées par la suite (mémorisées). Ils ne sont pas aussi difficiles à suivre que le sujet mais peuvent néanmoins conduire à des tests enchevêtrés et doivent donc être utilisés avec discrétion.
Le sujet est l'objet testé. RSpec a une idée explicite du sujet. Il peut être défini ou non. Si tel est le cas, RSpec peut appeler des méthodes dessus sans y faire explicitement référence.
Par défaut, si le premier argument d'un groupe d'exemple le plus externe (bloc describe
ou context
) est une classe, RSpec crée une instance de cette classe et l'affecte au sujet. Par exemple, les passes suivantes:
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
Vous pouvez définir le sujet vous-même avec subject
:
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Vous pouvez donner un nom au sujet lorsque vous le définissez:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
Même si vous nommez le sujet, vous pouvez toujours vous y référer anonymement:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Vous pouvez définir plusieurs sujets nommés. Le sujet nommé le plus récemment défini est l'anonyme subject
.
Cependant le sujet est défini,
C'est instancié paresseusement. C'est-à-dire que l'instanciation implicite de la classe décrite ou l'exécution du bloc passé à subject
ne se produit que lorsque subject
est mentionné ou que le sujet nommé est mentionné dans un exemple. Si vous voulez que votre sujet explicite soit instancié avec impatience (avant qu'un exemple de son groupe ne s'exécute), dites subject!
au lieu de subject
.
Les attentes peuvent être définies de manière implicite (sans écrire subject
ou le nom d'un sujet nommé):
describe A do
it { is_expected.to be_an(A) }
end
Le sujet existe pour supporter cette syntaxe à une ligne.
Un subject
implicite (déduit de l’exemple de groupe) est difficile à comprendre car
is_expected
sans destinataire explicite) ou explicitement (comme subject
), il ne donne au lecteur aucune information sur le rôle ou la nature de l’objet sur lequel l’attente est appelée.it
dans la syntaxe d'exemple normale), de sorte que la seule information dont dispose le lecteur sur le but de l'exemple est l'attente elle-même.Par conséquent, l'utilisation d'un sujet implicite n'est utile que lorsque le contexte est susceptible d'être bien compris par tous les lecteurs et qu'il n'est vraiment pas nécessaire de donner un exemple de description. Le cas canonique teste les validations ActiveRecord avec des correspondants:
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
Une explication anonyme subject
(définie avec subject
sans nom) est un peu meilleure, car le lecteur peut voir comment elle est instanciée, mais
Un sujet nommé fournit un nom révélateur d'intention, mais la seule raison d'utiliser un sujet nommé au lieu d'une variable let
est si vous souhaitez utiliser le sujet anonyme de temps en temps, et nous venons d'expliquer pourquoi la valeur anonyme le sujet est difficile à comprendre.
Donc, les utilisations légitimes d’un subject
ou d’un sujet nommé anonymes explicites sont très rares.
let
Les variables let
ressemblent aux sujets nommés, à deux différences près:
let
/let!
au lieu de subject
/subject!
subject
et ne permettent pas d'appeler implicitement les attentes.Il est tout à fait légitime d'utiliser let
pour réduire le double emploi entre les exemples. Toutefois, ne le faites que s'il ne sacrifie pas la clarté du test. Le moment le plus sûr pour utiliser let
est quand le but de la variable let
est clairement défini par son nom (pour que le lecteur n'ait pas à trouver la définition, qui pourrait être éloignée de plusieurs lignes, pour comprendre chaque exemple) et qu'il est utilisé de la même manière dans chaque exemple. Si l'une ou l'autre de ces choses n'est pas vraie, envisagez de définir l'objet dans une ancienne variable locale simple ou d'appeler une méthode factory directement dans l'exemple.
let!
_ est risqué, car ce n’est pas paresseux. Si quelqu'un ajoute un exemple au groupe d'exemples qui contient le let!
, mais l'exemple n'a pas besoin de let!
variable,
let!
variable et me demande si et comment cela affecte l'exemplelet!
variablleAlors utilisez let!
_, le cas échéant, uniquement dans de petits groupes d’exemples simples où il est moins probable que les futurs auteurs d’exemples tombent dans ce piège.
Il existe une surutilisation fréquente de sujets ou de variables let
qui mérite d’être examinée séparément. Certaines personnes aiment les utiliser comme ceci:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(Ceci est un exemple simple d’une méthode qui renvoie un nombre pour lequel nous avons besoin de deux attentes, mais ce style peut avoir beaucoup plus d’exemples/d’attentes si la méthode renvoie une valeur plus complexe qui nécessite de nombreuses attentes et/ou a de nombreux effets secondaires qui tous ont besoin d'attentes.)
Les gens le font parce qu’ils ont entendu dire qu’il ne devrait y avoir qu’une attente par exemple (ce qui est mélangé à la règle valide selon laquelle on ne devrait tester qu’un appel de méthode par exemple) ou parce qu’ils adorent la ruse de RSpec. Ne le faites pas, que ce soit avec un sujet anonyme ou nommé ou avec une variable let
! Ce style a plusieurs problèmes:
Au lieu de cela, écrivez un seul exemple:
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
Subject
et let
ne sont que des outils pour vous aider à ranger et à accélérer vos tests. Les membres de la communauté rspec les utilisent donc je ne me soucierais pas de savoir si on peut les utiliser ou non. Ils peuvent être utilisés de la même manière mais servent des objectifs légèrement différents
Subject
vous permet de déclarer un sujet de test, puis de le réutiliser pour un nombre quelconque de tests suivants. Cela réduit la répétition de code (séchez votre code)
Let
est une alternative à before: each
blocs, qui attribuent des données de test aux variables d'instance. Let
vous donne quelques avantages. Tout d'abord, il met en cache la valeur sans l'affecter à une variable d'instance. Deuxièmement, il est évalué paresseusement, ce qui signifie qu'il n'est pas évalué jusqu'à ce qu'une spécification le demande. Ainsi, let
vous aide à accélérer vos tests. Je pense aussi que let
est plus facile à lire
subject
est ce qui est testé, généralement une instance ou une classe. let
permet d'affecter des variables à vos tests, qui sont évaluées paresseusement et non à l'aide de variables d'instance. Il y a quelques bons exemples dans ce fil.