Question Quand utiliser RSpec let ()?


J'ai tendance à utiliser avant les blocs pour définir des variables d'instance. J'utilise ensuite ces variables dans mes exemples. Je suis récemment tombé sur let(). Selon RSpec docs, il est habitué à

... pour définir une méthode d'aide mémo. La valeur sera mise en cache sur plusieurs appels dans le même exemple mais pas entre les exemples.

En quoi est-ce différent d'utiliser des variables d'instance avant les blocs? Et aussi quand devriez-vous utiliser let() contre before()?


413
2018-03-19 02:10


origine


Réponses:


Je préfère toujours let à une variable d'instance pour deux raisons:

  • Les variables d'instance apparaissent dès qu'elles sont référencées. Cela signifie que si vous mettez le doigt sur l’orthographe de la variable d’instance, une nouvelle sera créée et initialisée à nil, ce qui peut conduire à des bogues et des faux positifs subtils. Depuis let crée une méthode, vous aurez un NameError quand vous l'orthographiez mal, ce que je trouve préférable. Il est également plus facile de refactoriser les spécifications.
  • UNE before(:each) hook s'exécutera avant chaque exemple, même si l'exemple n'utilise aucune des variables d'instance définies dans le hook. Ce n'est généralement pas un gros problème, mais si la configuration de la variable d'instance prend beaucoup de temps, vous perdez des cycles. Pour la méthode définie par let, le code d'initialisation ne s'exécute que si l'exemple l'appelle.
  • Vous pouvez refactoriser à partir d'une variable locale dans un exemple directement dans un let sans changer le référençant la syntaxe dans l'exemple. Si vous refactorez une variable d’instance, vous devez changer comment vous référencez l'objet dans l'exemple (par exemple, ajoutez un @).
  • C'est un peu subjectif, mais comme Mike Lewis l'a souligné, je pense que cela rend la spécification plus facile à lire. J'aime l'organisation de définir tous mes objets dépendants avec let et garder mon it bloc agréable et court.

561
2018-03-19 04:04



La différence entre l'utilisation de variables d'instance et let() est-ce let() est paresseux-évalué. Cela signifie que let() n'est pas évalué tant que la méthode qu'il définit n'est pas exécutée pour la première fois.

La différence entre before et let est-ce let() vous donne une belle façon de définir un groupe de variables dans un style «en cascade». Ce faisant, la spécification est un peu meilleure en simplifiant le code.


75
2018-03-19 02:48



J'ai complètement remplacé toutes les utilisations de variables d'instance dans mes tests rspec pour utiliser let (). J'ai écrit un exemple rapide pour un ami qui l'utilisait pour enseigner une petite classe Rspec: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html

Comme certaines autres réponses l’indiquent ici, let () est évalué paresseux pour ne charger que celles qui nécessitent un chargement. Il sèche la spécification et la rend plus lisible. J'ai en fait porté le code let () de Rspec à utiliser dans mes contrôleurs, dans le style de la gemme inherited_resource. http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html

Avec l'évaluation paresseuse, l'autre avantage est que, combiné avec ActiveSupport :: Concern, et le chargement-tout-en-spec / support / comportement, vous pouvez créer votre propre mini-DSL spec spécifique à votre application. J'en ai écrit d'autres pour tester les ressources Rack et RESTful.

La stratégie que j'utilise est Factory-Everything (via Machinist + Forgery / Faker). Cependant, il est possible de l'utiliser en combinaison avec des blocs before (: each) pour précharger des usines pour un ensemble complet de groupes d'exemples, ce qui permet aux spécifications de s'exécuter plus rapidement: http://makandra.com/notes/770-supprimer-advantage-of-rspec-s-let-in-before-blocks


16
2018-03-19 19:12



Il est important de garder à l'esprit que laisser est paresseux évalué et ne pas mettre des méthodes d'effets secondaires dans le cas contraire, vous ne seriez pas en mesure de changer de laisser à avant (: chacun) facilement. Vous pouvez utiliser laisser! au lieu de laisser afin qu'il soit évalué avant chaque scénario.


13
2017-08-12 20:50



En général, let() est une syntaxe plus agréable, et vous enregistrez @name symboles partout. Mais, caveat emptor! j'ai trouvé let() introduit aussi des bugs subtils (ou au moins des grattements de tête) car la variable n'existe pas vraiment jusqu'à ce que vous essayiez de l'utiliser ... Dites le signe du conte: si vous ajoutez un puts après le let() voir que la variable est correcte permet à une spécification de passer, mais sans la puts la spécification échoue - vous avez trouvé cette subtilité.

J'ai aussi trouvé que let() ne semble pas mettre en cache en toutes circonstances! Je l'ai écrit sur mon blog: http://technicaldebt.com/?p=1242

Peut-être que c'est juste moi?


7
2018-04-03 13:37



let est fonctionnel comme son essentiellement un Proc. Aussi son caché.

Un piège que j'ai trouvé tout de suite avec let ... Dans un bloc Spec qui évalue un changement.

let(:object) {FactoryGirl.create :object}

expect {
  post :destroy, id: review.id
}.to change(Object, :count).by(-1)

Vous devez être sûr d'appeler let en dehors de votre attente c'est-à-dire que vous appelez FactoryGirl.create dans votre bloc let. Je le fais généralement en vérifiant que l'objet est persisté.

object.persisted?.should eq true

Sinon, quand le let Le bloc est appelé la première fois qu'un changement dans la base de données se produira en raison de l'instanciation paresseuse.

Mettre à jour

Juste en ajoutant une note. Soyez prudent en jouant code golf ou dans ce cas rspec golf avec cette réponse.

Dans ce cas, je dois juste appeler une méthode à laquelle l'objet répond. Donc j'invoque le _.persisted?_ méthode sur l'objet comme sa vérité. Tout ce que j'essaie de faire est d'instancier l'objet. Vous pourriez appeler vide? ou nul? aussi. Le point n'est pas le test, mais en apportant l'objet de la vie en l'appelant.

Donc, vous ne pouvez pas refactoriser

object.persisted?.should eq true

être

object.should be_persisted 

comme l'objet n'a pas été instancié ... c'est paresseux. :)

Mise à jour 2

tirer parti de la laisser! syntaxe pour la création instantanée d'objets, ce qui devrait éviter ce problème. Notez bien que cela vaincra une grande partie du but de la paresse du let non-banged.

Aussi, dans certains cas, vous pourriez réellement vouloir tirer parti de la syntaxe du sujet au lieu de laisser comme il peut vous donner des options supplémentaires.

subject(:object) {FactoryGirl.create :object}

5
2018-05-07 19:32



Remarque à Joseph - si vous créez des objets de base de données dans un before(:all) ils ne seront pas capturés dans une transaction et vous êtes beaucoup plus susceptibles de laisser des traces dans votre base de données de test. Utilisation before(:each) au lieu.

L'autre raison d'utiliser let et son évaluation paresseuse est que vous pouvez prendre un objet compliqué et tester des morceaux individuels en remplaçant les lets dans les contextes, comme dans cet exemple très artificiel:

context "foo" do
  let(:params) do
     { :foo => foo,  :bar => "bar" }
  end
  let(:foo) { "foo" }
  it "is set to foo" do
    params[:foo].should eq("foo")
  end
  context "when foo is bar" do
    let(:foo) { "bar" }
    # NOTE we didn't have to redefine params entirely!
    it "is set to bar" do
      params[:foo].should eq("bar")
    end
  end
end

2
2017-08-28 16:50



"avant" par défaut implique before(:each). Ref The Rspec Book, copyright 2010, page 228.

before(scope = :each, options={}, &block)

j'utilise before(:each) pour semer des données pour chaque groupe d’exemples sans avoir à appeler le let méthode pour créer les données dans le bloc "it". Moins de code dans le bloc "it" dans ce cas.

j'utilise let si je veux des données dans certains exemples mais pas d'autres.

Les deux avant et laisser sont parfaits pour sécher les blocs "it".

Pour éviter toute confusion, "let" est différent de before(:all). "Let" réévalue sa méthode et sa valeur pour chaque exemple ("it"), mais met en cache la valeur sur plusieurs appels dans le même exemple. Vous pouvez en lire plus à ce sujet ici: https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let 


1
2018-02-21 20:56