如何在RSpec中说"any_instance""should_receive"任意次

Har*_*Wit 68 testing rspec ruby-on-rails mocking mocha.js

我在rails中有一个导入控制器,它将多个带有多条记录的csv文件导入到我的数据库中.我想在RSpec中测试是否使用RSpec实际保存了记录:

<Model>.any_instance.should_receive(:save).at_least(:once)
Run Code Online (Sandbox Code Playgroud)

但是我得到错误说:

The message 'save' was received by <model instance> but has already been received by <another model instance>
Run Code Online (Sandbox Code Playgroud)

一个人为的控制器示例:

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
Run Code Online (Sandbox Code Playgroud)

是否可以使用RSpec进行测试或是否有解决方法?

Rob*_*Rob 45

这是一个更好的答案,避免必须覆盖: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
Run Code Online (Sandbox Code Playgroud)

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

更新 - 新的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
Run Code Online (Sandbox Code Playgroud)


mui*_*bot 44

有一个新的语法:

expect_any_instance_of(Model).to receive(:save).at_least(:once)
Run Code Online (Sandbox Code Playgroud)

  • 这实际上对我不起作用; 与原始问题相同的错误.使用RSpec 3.3.3. (18认同)
  • rspec的设计者不鼓励使用`expect_any_instance_of`.这是[指向文档的链接](https://relishapp.com/rspec/rspec-mocks/docs/working-with-legacy-code/any-instance). (3认同)

Har*_*Wit 15

我终于设法做了一个适合我的测试:

  mutation = FactoryGirl.build(:mutation)
  Mutation.stub(:new).and_return(mutation)
  mutation.should_receive(:save).at_least(:once)
Run Code Online (Sandbox Code Playgroud)

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

  • 议员.太糟糕了,你不能说"many_instances". (3认同)

mic*_*lpm 11

就像这样

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

那么期待这样:

# User.any_instance.should_receive(:save).at_least(:once)
User.should_receive(:save).at_least(:once)
Run Code Online (Sandbox Code Playgroud)

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


sp8*_*p89 9

这是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
Run Code Online (Sandbox Code Playgroud)


Ric*_*oor 5

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

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