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?
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:
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é :)
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
.
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.
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.