Ruby-on-rails 如何在 RSpec 中多次说“any_instance”“should_receive”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9800992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 03:05:07  来源:igfitidea点击:

How to say "any_instance" "should_receive" any number of times in RSpec

ruby-on-railstestingrspecmockingmocha

提问by Harm de Wit

I've got an import controller in rails that imports several csv files with multiple records into my database. I would like to test in RSpec if the records are actually saved by using RSpec:

我在 rails 中有一个导入控制器,可以将多个包含多条记录的 csv 文件导入到我的数据库中。我想在 RSpec 中测试是否确实使用 RSpec 保存了记录:

<Model>.any_instance.should_receive(:save).at_least(:once)

However i get the error saying:

但是我收到错误消息:

The message 'save' was received by <model instance> but has already been received by <another model instance>

A contrived example of the controller:

控制器的一个人为例子:

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

Is it possible to test this using RSpec or is there any workaround?

是否可以使用 RSpec 对此进行测试,或者是否有任何解决方法?

采纳答案by muirbot

There's a new syntax for this:

有一个新的语法:

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

回答by Rob

Here's a better answer that avoids having to override the :new method:

这是一个更好的答案,可以避免重写 :new 方法:

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

Seems that the stub method can be attached to any instance w/o the constraint, and the do block can make a count that you can check to assert it was called the right number of times.

似乎存根方法可以附加到没有约束的任何实例,并且 do 块可以进行计数,您可以检查它以断言它被调用的次数是正确的。

Update - new rspec version requires this syntax:

更新 - 新的 rspec 版本需要以下语法:

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

回答by Harm de Wit

I finally managed to make a test that works for me:

我终于设法进行了一个对我有用的测试:

  mutation = FactoryGirl.build(:mutation)
  Mutation.stub(:new).and_return(mutation)
  mutation.should_receive(:save).at_least(:once)

The stub method returns one single instance that receives the save method multiple times. Because it is a single instance i can drop the any_instancemethod and use the at_leastmethod normally.

存根方法返回一个多次接收 save 方法的单个实例。因为它是单个实例,所以我可以删除该any_instance方法并at_least正常使用该方法。

回答by michelpm

Stub like this

像这样的存根

User.stub(:save) # Could be any class method in any class
User.any_instance.stub(:save) { |*args| User.save(*args) }

Then expect like this:

然后期待这样:

# User.any_instance.should_receive(:save).at_least(:once)
User.should_receive(:save).at_least(:once)

This is a simplification of this gist, to use any_instance, since you don't need to proxy to the original method. Refer to that gist for other uses.

这是这个要点的简化,使用any_instance,因为您不需要代理到原始方法。有关其他用途,请参阅该要点。

回答by sp89

This is Rob's example using RSpec 3.3, which no longer supports Foo.any_instance. I found this useful when in a loop creating objects

这是 Rob 使用 RSpec 3.3 的示例,它不再支持 Foo.any_instance. 我发现这在循环创建对象时很有用

# 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

回答by Rick Pastoor

My case was a bit different, but I ended up at this question to figured to drop my answer here too. In my case I wanted to stub any instance of a given class. I got the same error when I used expect_any_instance_of(Model).to. When I changed it to allow_any_instance_of(Model).to, my problem was solved.

我的情况有点不同,但我最终在这个问题上想把我的答案也放在这里。就我而言,我想存根给定类的任何实例。当我使用expect_any_instance_of(Model).to. 当我将其更改为 时allow_any_instance_of(Model).to,我的问题就解决了。

Check out the documentation for some more background: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class

查看文档了解更多背景信息:https: //github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class

回答by Brazhnyk Yuriy

You may try to count the number of newon the class. That is not actually tests the number of saves but may be enough

你可以试着数一数new班上的人数。这实际上不是测试saves的数量,但可能就足够了

    expect(Mutation).to receive(:new).at_least(:once)

If there is the only expectation of how many times it was saved. Then you probably want to use spy()instead of fully functioning factory, as in Harm de Witown answer

如果有唯一的期望它被保存了多少次。那么你可能想要使用spy()而不是功能齐全的工厂,如Harm de Wit自己的答案

    allow(Mutation).to receive(:new).and_return(spy)
    ...
    expect(Mutation.new).to have_received(:save).at_least(:once)