web-dev-qa-db-fra.com

Comment dire "any_instance" "should_receive" un certain nombre de fois dans RSpec

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?

65
Harm de Wit

Il y a une nouvelle syntaxe pour cela:

expect_any_instance_of(Model).to receive(:save).at_least(:once)
45
muirbot

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
45
Rob

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.

15
Harm de Wit

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.

10
michelpm

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
8
sp89

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

2
Rick Pastoor

Vous pouvez essayer de compter le nombre de new sur la classe. Cela ne teste pas réellement le nombre de saves 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)
0
Brazhnyk Yuriy