web-dev-qa-db-fra.com

Quelle est la différence entre le sujet et la let de RSpec? Quand doivent-ils être utilisés ou non?

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.

58
new2cpp

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.

L'objet

Comment ça marche

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,

  1. 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.

  2. 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.

Quand l'utiliser

Un subject implicite (déduit de l’exemple de groupe) est difficile à comprendre car

  • C'est instancié dans les coulisses.
  • Qu'il soit utilisé implicitement (en appelant 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.
  • La syntaxe d'exemple one-liner ne comporte pas de description d'exemple (l'argument de chaîne à 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

  • il peut toujours éloigner l'instanciation du sujet de l'endroit où il est utilisé (par exemple, au sommet d'un groupe d'exemples contenant de nombreux exemples qui l'utilisent), ce qui reste difficile à suivre, et
  • il a les autres problèmes que le sujet implicite fait.

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.

Variables let

Comment ils travaillent

Les variables let ressemblent aux sujets nommés, à deux différences près:

  • ils sont définis avec let/let! au lieu de subject/subject!
  • ils ne définissent pas l'anonyme subject et ne permettent pas d'appeler implicitement les attentes.

Quand les utiliser

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,

  • cet exemple sera difficile à comprendre, car le lecteur verra le let! variable et me demande si et comment cela affecte l'exemple
  • l'exemple sera plus lent que nécessaire, en raison du temps nécessaire pour créer le let! variablle

Alors 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.

Le fétiche avec une seule attente par exemple

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:

  • Le sujet anonyme n'est pas le sujet des exemples - la méthode est le sujet. Ecrire le test de cette façon bousille le langage, ce qui rend plus difficile la réflexion.
  • Comme toujours avec des exemples d'une ligne, il n'y a pas de place pour expliquer le sens des attentes.
  • Le sujet doit être construit pour chaque exemple, ce qui est lent.

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
120
Dave Schweisguth

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

3
Ren

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.

https://github.com/reachlocal/rspec-style-guide/issues/6

1
nikkypx