Supposons que j'ai la spécification suivante:
...
describe Thing do
it 'can read data' do
@data = get_data_from_file # [ '42', '36' ]
expect(@data.count).to eq 2
end
it 'can process data' do
expect(@data[0].to_i).to eq 42 # Fails because @data is nil
end
end
...
Tout ce que je voulais, c'est avoir une variable partagée dans le décrire ou contexte donné. J'écrirais une valeur dans un exemple et la lirais dans un autre. Comment je fais ça?
Vous devez utiliser le bloc before(:each)
ou before(:all)
:
describe Thing do
before(:each) do
@data = get_data_from_file # [ '42', '36' ]
end
it 'can read data' do
expect(@data.count).to eq 2
end
it 'can process data' do
expect(@data[0].to_i).to eq 42
end
end
La différence est que before(:each)
sera exécutée séparément pour chaque cas et before(:all)
une fois avant tous les exemples de ce describe/context
. Je vous recommande de préférer before(:each)
à before(:all)
, car chaque exemple sera isolé dans ce cas, ce qui est une bonne pratique.
Il existe de rares cas où vous souhaitez utiliser before(:all)
, par exemple si votre get_data_from_file
A un temps d'exécution long, dans ce cas, vous pouvez, bien sûr, sacrifier l'isolement des tests au profit de la vitesse. Mais je tiens à vous informer que lorsque vous utilisez before(:all)
, la modification de votre variable @data
Dans un test (bloc it
) entraînera des conséquences inattendues pour d'autres tests dans describe/context
Portée car ils la partageront.
before(:all)
exemple:
describe MyClass do
before(:all) do
@a = []
end
it { @a << 1; p @a }
it { @a << 2; p @a }
it { @a << 3; p @a }
end
Sortira:
[1]
[1, 2]
[1, 2, 3]
[~ # ~] mis à jour [~ # ~]
Pour répondre à votre question
describe MyClass do
before(:all) do
@a = []
end
it { @a = [1]; p @a }
it { p @a }
end
Sortira
[1]
[]
Parce que dans le premier it
vous affectez localement la variable d'instance @a, donc ce n'est pas la même chose avec @a dans le bloc before(:all)
et n'est pas visible pour les autres blocs it
, vous pouvez le vérifier en sortant object_id
s. Donc, seule la modification fera l'affaire, l'affectation entraînera la création de nouveaux objets.
Donc, si vous attribuez des variables plusieurs fois, vous devriez probablement vous retrouver avec un bloc it
et plusieurs attentes. C'est acceptable, selon les meilleures pratiques.
C'est vraiment le but de RSpec let helper qui vous permet de le faire avec votre code:
...
describe Thing do
let(:data) { get_data_from_file }
it 'can read data' do
expect(data.count).to eq 2
end
it 'can process data' do
expect(data[0].to_i).to eq 42
end
end
...
Je viens de rencontrer ce même problème. Comment j'ai résolu le problème en utilisant factory_girl gem.
Voici les bases:
créer une usine (voici un extrait de code:
require 'factory_girl'
require 'faker' # you can use faker, if you want to use the factory to generate fake data
FactoryGirl.define do
factory :generate_data, class: MyModule::MyClass do
key 'value'
end
end
Maintenant, après avoir fait l'usine, vous devez créer un modèle qui ressemble à ceci:
Module MyModule
class MyClass
attr_accessor :key
#you can also place methods here to call from your spec test, if you wish
# def self.test
#some test
# end
end
end
Maintenant, pour revenir à votre exemple, vous pouvez faire quelque chose comme ceci:
describe Thing do
before(:all) do
@data = FactoryGirl.build(:generate_data)
end
it 'can read data' do
@data.key = get_data_from_file # [ '42', '36' ]
expect(@data.key.count).to eq 2
end
it 'can process data' do
expect(@data.key[0].to_i).to eq 42 # @data will not be nil. at this point. whatever @data.key is equal to last which was set in your previous context will be what data.key is here
end
end
Quoi qu'il en soit, bonne chance, dites-nous si vous avez une autre solution!