J'ai un contrôleur d'importation dans Rails qui importe plusieurs fichiers csv avec plusieurs enregistrements dans ma base de données. Je voudrais tester dans RSpec si les enregistrements sont réellement enregistrés en utilisant RSpec:
<Model>.any_instance.should_receive(:save).at_least(:once)
Cependant, je reçois l'erreur en disant:
The message 'save' was received by <model instance> but has already been received by <another model instance>
Un exemple artificiel du contrôleur:
rows = CSV.parse(uploaded_file.tempfile, col_sep: "|")
ActiveRecord::Base.transaction do
rows.each do |row|
mutation = Mutation.new
row.each_with_index do |value, index|
Mutation.send("#{attribute_order[index]}=", value)
end
mutation.save
end
Est-il possible de tester cela en utilisant RSpec ou existe-t-il une solution?
Il y a une nouvelle syntaxe pour cela:
expect_any_instance_of(Model).to receive(:save).at_least(:once)
Voici une meilleure réponse qui évite d'avoir à remplacer la: nouvelle méthode:
save_count = 0
<Model>.any_instance.stub(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
Il semble que la méthode stub puisse être attachée à n'importe quelle instance sans la contrainte, et le bloc do peut effectuer un décompte que vous pouvez vérifier pour affirmer qu'il a été appelé le bon nombre de fois.
Mise à jour - la nouvelle version de rspec nécessite cette syntaxe:
save_count = 0
allow_any_instance_of(Model).to receive(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
J'ai finalement réussi à faire un test qui fonctionne pour moi:
mutation = FactoryGirl.build(:mutation)
Mutation.stub(:new).and_return(mutation)
mutation.should_receive(:save).at_least(:once)
La méthode stub renvoie une seule instance qui reçoit plusieurs fois la méthode save. Parce qu'il s'agit d'une seule instance, je peux supprimer le any_instance
et utilisez la méthode at_least
méthode normalement.
Stub comme ça
User.stub(:save) # Could be any class method in any class
User.any_instance.stub(:save) { |*args| User.save(*args) }
Attendez-vous ensuite à ceci:
# User.any_instance.should_receive(:save).at_least(:once)
User.should_receive(:save).at_least(:once)
Ceci est une simplification de this Gist , pour utiliser any_instance
, car vous n'avez pas besoin de proxy pour la méthode d'origine. Reportez-vous à ce Gist pour d'autres utilisations.
Voici l'exemple de Rob utilisant RSpec 3.3, qui ne prend plus en charge Foo.any_instance
. J'ai trouvé cela utile dans une boucle créant des objets
# code (simplified version)
array_of_hashes.each { |hash| Model.new(hash).write! }
# spec
it "calls write! for each instance of Model" do
call_count = 0
allow_any_instance_of(Model).to receive(:write!) { call_count += 1 }
response.process # run the test
expect(call_count).to eq(2)
end
Mon cas était un peu différent, mais je me suis retrouvé à cette question pour penser à déposer ma réponse ici aussi. Dans mon cas, je voulais écraser toute instance d'une classe donnée. J'ai eu la même erreur lorsque j'ai utilisé expect_any_instance_of(Model).to
. Quand je l'ai changé en allow_any_instance_of(Model).to
, mon problème a été résolu.
Consultez la documentation pour plus d'informations: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class
Vous pouvez essayer de compter le nombre de new
sur la classe. Cela ne teste pas réellement le nombre de save
s mais peut être suffisant
expect(Mutation).to receive(:new).at_least(:once)
S'il y a la seule attente de combien de fois il a été enregistré. Ensuite, vous voudrez probablement utiliser spy()
au lieu d'une usine entièrement fonctionnelle, comme dans Harm de Wit
Propre réponse
allow(Mutation).to receive(:new).and_return(spy)
...
expect(Mutation.new).to have_received(:save).at_least(:once)