Quelle est la différence entre let
et un before
bloc dans RSpec?
Et quand les utiliser?
Quelle sera la bonne approche (laissez 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é ce post de stackoverflow
Mais est-il bon de définir des trucs d'association comme ci-dessus?
ruby-on-rails
unit-testing
rspec
Kriysna
la source
la source
Réponses:
Les gens semblent avoir expliqué certaines des façons fondamentales dont ils diffèrent, mais les ont laissés de côté
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 , je ne les mentionnerai donc pas ici.
laissez les blocs
Le code à l'intérieur d'un
let
bloc n'est exécuté que lorsqu'il est référencé, le chargement différé signifie que l'ordre de ces blocs n'est pas pertinent. Cela vous donne une grande quantité d'énergie pour réduire les configurations répétées grâce à vos spécifications.Un exemple (extrêmement petit et artificiel) de ceci est:
Vous pouvez voir que cela
has_moustache
est défini différemment dans chaque cas, mais il n'est pas nécessaire de répéter lasubject
définition. Il est important de noter que le dernierlet
bloc 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
if passé unperson
modèle avectop_hat
la valeur true, mais aucune moustache ne serait:Une autre chose à noter à propos des blocs let, ils ne doivent pas être utilisés si vous recherchez quelque chose qui a été sauvegardé dans la base de données (c'est-à-dire
Library.find_awesome_people(search_criteria)
) car ils ne seront pas sauvegardés dans la base de données à moins qu'ils n'aient déjà été référencés.let!
ou desbefore
blocs sont ce qui devrait être utilisé ici.Aussi, ne l' utilisez jamais
before
pour déclencher l'exécution delet
blocs, c'est pour cela qu'illet!
est fait!laisser! blocs
let!
les blocs sont exécutés dans l'ordre dans lequel ils sont définis (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 revenir aux variables d'instance.Comme pour les
let
blocs, si plusieurslet!
blocs 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 leslet!
blocs seront exécutés plusieurs fois s'ils sont utilisés de cette manière, alors que lelet
bloc ne s'exécutera que la dernière fois.avant (: chaque) blocs
before(:each)
est le bloc par défaut avant, et peut donc être référencé commebefore {}
plutôt que de spécifier le plein àbefore(:each) {}
chaque fois.C'est ma préférence personnelle d'utiliser des
before
blocs dans quelques situations fondamentales. J'utiliserai avant les blocs si:before { get :index }
). Même si vous pouvez l'utilisersubject
dans de nombreux cas, cela semble parfois plus explicite si vous n'avez pas besoin d'une référence.Si vous vous retrouvez à écrire de gros
before
blocs pour vos spécifications, vérifiez vos usines et assurez-vous de bien comprendre les caractéristiques et leur flexibilité.avant (: tous) blocs
Celles-ci ne sont exécutées qu'une seule fois, avant les spécifications dans le contexte actuel (et ses enfants). Ceux-ci peuvent être utilisés à bon escient s'ils sont écrits correctement, car dans certaines situations, cela peut réduire l'exécution et les efforts.
Un exemple (qui n'affecterait guère le temps d'exécution) consiste à simuler une variable ENV pour un test, ce que vous ne devriez jamais avoir à faire qu'une seule fois.
J'espère que cela pourra aider :)
la source
before(:all)
option n'existe pas dans Minitest. Voici une solution de contournement dans les commentaires: github.com/seattlerb/minitest/issues/61its
n'est plus dans rspec-core. Une manière plus moderne et idiomatique estit { is_expected.to be_awesome }
.Presque toujours, je préfère
let
. Le message que vous liez spécifie qu'illet
est également plus rapide. Cependant, parfois, lorsque de nombreuses commandes doivent être exécutées, je pourrais l'utiliserbefore(: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 debefore(:each)
. De manière générale, quand une simple initialisation de variable est effectuée, j'ai tendance à aimer utiliserlet
.la source
Une grande différence qui n'a pas été mentionnée est que les variables définies avec
let
ne sont instanciées que lorsque vous l'appelez pour la première fois. Ainsi, même si unbefore(:each)
bloc 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 définir un certain nombre delet
variables, puis utiliser unbefore(:each)
bloc pour appeler chaquelet
instance juste pour vous assurer que les données sont disponibles pour commencer.la source
let!
pour définir des méthodes appelées avant chaque exemple. Voir la documentation RSpec .Il semble que vous utilisiez Machinist. Attention, vous pouvez rencontrer des 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.
la source