web-dev-qa-db-fra.com

RSpec: Quelle est la différence entre let et un bloc before?

Quelle est la différence entre let et un bloc before dans RSpec?

Et quand utiliser chacun?

Quelle sera la bonne approche (let ou avant) dans l'exemple ci-dessous?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

J'ai étudié cela stackoverflow post

Mais est-il bon de définir des choses comme pour l'association comme ci-dessus?

86
kriysna

Les gens semblent avoir expliqué certaines des différences de base, mais ont omis before(:all) et n'expliquent pas exactement pourquoi ils devraient être utilisés.

Je pense que les variables d'instance n'ont pas leur place dans la grande majorité des spécifications, en partie pour les raisons mentionnées dans cette réponse , donc je ne les mentionnerai pas comme option ici.

laissez les blocs

Le code dans un bloc let n'est exécuté que lorsqu'il est référencé, un chargement paresseux signifie que l'ordre de ces blocs n'est pas pertinent. Cela vous donne une grande quantité d'énergie pour réduire la configuration répétée de vos spécifications.

Un exemple (extrêmement petit et artificiel) de ceci est:

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end

Vous pouvez voir que has_moustache Est défini différemment dans chaque cas, mais il n'est pas nécessaire de répéter la définition de subject. Il est important de noter que le dernier bloc let défini dans le contexte actuel sera utilisé. C'est bon pour définir une valeur par défaut à utiliser pour la majorité des spécifications, qui peuvent être écrasées si nécessaire.

Par exemple, vérifier la valeur de retour de calculate_awesome Si passé un modèle person avec top_hat Défini sur true, mais aucune moustache ne serait:

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end

Une autre chose à noter sur les blocs let, ils ne doivent pas être utilisés si vous recherchez quelque chose qui a été enregistré dans la base de données (c'est-à-dire Library.find_awesome_people(search_criteria)) car ils ne seront pas enregistrés dans la base de données à moins qu'ils ne l'aient déjà été référencé. Les blocs let! Ou before doivent être utilisés ici.

De plus, jamais jamais utilisez before pour déclencher l'exécution des blocs let, c'est pour ça que let! est fait!

laissez! blocs

Les blocs let! Sont exécutés dans l'ordre de leur définition (un peu comme un bloc avant). La seule différence fondamentale avec les blocs avant est que vous obtenez une référence explicite à cette variable, plutôt que de devoir recourir aux variables d'instance.

Comme pour les blocs let, si plusieurs blocs let! Sont définis avec le même nom, le plus récent est celui qui sera utilisé lors de l'exécution. La principale différence est que les blocs let! Seront exécutés plusieurs fois s'ils sont utilisés de cette façon, tandis que le bloc let ne s'exécutera que la dernière fois.

avant (: chaque) blocs

before(:each) est le bloc avant par défaut, et peut donc être référencé comme before {} plutôt que de spécifier le before(:each) {} complet à chaque fois.

C'est ma préférence personnelle d'utiliser des blocs before dans quelques situations principales. J'utiliserai avant les blocs si:

  • J'utilise moqueur, stubbing ou double
  • Il existe une configuration de taille raisonnable (généralement, c'est un signe que vos caractéristiques d'usine n'ont pas été configurées correctement)
  • Il y a un certain nombre de variables que je n'ai pas besoin de référencer directement, mais qui sont nécessaires pour la configuration
  • J'écris des tests de contrôleur fonctionnel dans Rails, et je veux exécuter une requête spécifique à tester (c'est-à-dire before { get :index }). Même si vous pouvez utiliser subject pour cela dans de nombreux cas, cela semble parfois plus explicite si vous n'avez pas besoin d'une référence.

Si vous vous retrouvez en train d'écrire de gros blocs before pour vos spécifications, vérifiez vos usines et assurez-vous de bien comprendre les caractéristiques et leur flexibilité.

avant (: tous) les blocs

Celles-ci ne sont exécutées qu'une seule fois, avant les spécifications dans le contexte actuel (et ses enfants). Celles-ci peuvent être utilisées très avantageusement si elles sont écrites correctement, car dans certaines situations cela peut réduire l'exécution et l'effort.

Un exemple (qui n'affecterait guère le temps d'exécution) est la simulation d'une variable ENV pour un test, ce que vous ne devriez faire qu'une seule fois.

J'espère que ça t'as aidé :)

134
Jay

Presque toujours, je préfère let. Le message que vous liez spécifie que let est également plus rapide. Cependant, parfois, lorsque de nombreuses commandes doivent être exécutées, je pourrais utiliser before(:each) car sa syntaxe est plus claire lorsque de nombreuses commandes sont impliquées.

Dans votre exemple, je préférerais certainement utiliser let au lieu de before(:each). De manière générale, lorsque seulement une initialisation variable est effectuée, j'ai tendance à utiliser let.

25
Spyros

Une grande différence qui n'a pas été mentionnée est que les variables définies avec let ne sont pas instanciées jusqu'à ce que vous l'appeliez la première fois. Ainsi, bien qu'un bloc before(:each) instancie toutes les variables, let vous permet de définir un certain nombre de variables que vous pourriez utiliser dans plusieurs tests, il ne les instancie pas automatiquement. Sans le savoir, vos tests pourraient revenir vous mordre si vous vous attendez à ce que toutes les données soient chargées à l'avance. Dans certains cas, vous pouvez même vouloir définir un certain nombre de variables let, puis utiliser un bloc before(:each) pour appeler chaque instance let juste pour vous assurer que les données sont disponibles pour commencer.

15
mikeweber

Il semble que vous utilisiez Machinist. Attention, vous pouvez voir quelques problèmes avec make! à l'intérieur de let (la version non-bang) se produisant en dehors de la transaction globale de fixture (si vous utilisez également des fixtures transactionnelles) corrompant ainsi les données pour vos autres tests.

3
Tim Connor