Ruby 期望模拟结果接收方法

Ruby 期望模拟结果接收方法,ruby,rspec,pundit,rspec-mocks,Ruby,Rspec,Pundit,Rspec Mocks,我试图模拟一个类,这样我可以期望它被实例化,然后调用某个方法 我试过: expect(我的策略)。 接收(新的)。 和_wrap |原始do |方法,*args| 期望(method.call(*args))接收(:show?)并调用\u original 结束 但我得到的只是: #RSpec::Mocks::VerifyingMessageExpection:0x0055e9ffd0b530的未定义方法“show” 我已经尝试过提供一个块并首先调用原始方法(new和show?,我必须首先绑定

我试图模拟一个类,这样我可以期望它被实例化,然后调用某个方法

我试过:

expect(我的策略)。
接收(新的)。
和_wrap |原始do |方法,*args|
期望(method.call(*args))接收(:show?)并调用\u original
结束
但我得到的只是:

#RSpec::Mocks::VerifyingMessageExpection:0x0055e9ffd0b530的未定义方法“show”

我已经尝试过提供一个块并首先调用原始方法(new和show?,我必须首先绑定它们),但是错误总是一样的

我知道<代码>除了<代码>的任何实例之外,但它被认为是代码气味,所以我试图找到另一种方法来正确地执行它

上下文:我有权威政策,我想检查它是否被调用

我也试过了,但有同样的错误:

ctor=policy\u class.method(:new)
expect(策略类)。
接收(新的)。
使用(用户、记录)do
期望(ctor.call(user,record))。接收(查询)。并调用原始
结束

您违反了我的保险单。新增的

new
的包装器不会返回新的MyPolicy对象。它返回要接收(:show?)的
expect(method.call(*args))的结果,以及作为messageexpection的\u call\u original

相反,您可以确保返回新对象时带有

或者用老式的方式

      allow(MyPolicy).to receive(:new)
        .and_wrap_original do |method, *args|
          obj = method.call(*args)
          expect(obj).to receive(:show?).and_call_original
          obj
        end

分开这两个步骤通常更简单。Mock MyPolicy.new返回特定对象,然后期望调用显示?在那个物体上

let(:policy) do
  # This calls the real MyPolicy.new because policy is referenced
  # when setting up the MyPolicy.new mock.
  MyPolicy.new
end

before do
  allow(MyPolicy).to receive(:new).and_return(policy)
end
    
it 'shows' do
  expect(policy).to receive(:show?).and_call_original
  MyPolicy.new.show?
end
这意味着MyPolicy.new总是返回相同的对象。这是测试的一个优势,但可能会破坏某些东西。这是更灵活的,因为它将脚手架从正在测试的内容中分离出来。脚手架可以重复使用

RSpec.describe SomeClass do
  let(:policy) {
    MyPolicy.new
  }
  let(:thing) {
    described_class.new
  }

  shared_context 'mocked MyPolicy.new' do
    before do
      allow(MyPolicy).to receive(:new).and_return(policy)
    end
  end
  
  describe '#some_method' do
    include_context 'mocked new'
    
    it 'shows a policy' do
      expect(policy).to receive(:show?).and_call_original

      thing.some_method
    end
  end
  
  describe '#other_method' do
    include_context 'mocked MyPolicy.new'
    
    it 'checks its policy' do
      expect(policy).to receive(:check)

      thing.other_method
    end
  end
end

最后,不可访问的构造函数调用对于测试来说都是一个头痛的问题,而且它们是不灵活的。这是一个无法覆盖的默认值

class SomeClass
  def some_method
    MyPolicy.new.show?
  end  
end
将其转换为具有默认值的访问器

class SomeClass
  attr_writer :policy
  
  def policy
    @policy ||= MyPolicy.new
  end
  
  def some_method
    policy.show?
  end  
end
现在可以在测试中或任何其他地方访问它

RSpec.describe SomeClass do
  let(:thing) {
    described_class.new
  }
  
  describe '#some_method' do    
    it 'shows its policy' do
      expect(thing.policy).to receive(:show?).and_call_original
      thing.some_method
    end
  end
end

这是最可靠的选择。

返回一个double并在那里添加一个期望值怎么样
expect(MyPolicy).to(receive(:new).and_return(double(:scope).点击{scope | expect(scope).to receive(:show?}))
@SebastianPalma在这种情况下可以调用原始方法吗?谢谢你的提示!可能会在
接收(:show)
之后添加
。并调用\u original
,因为如果实例化
MyPolicy
,您将接收不同的对象,因此您无论如何都需要设置两个期望值。我也尝试过,但没有成功。我添加了另一个尝试,我认为在上次更新中,该块是在
self
中设置期望值,而不是在调用
new
policy\u类
返回的值中设置期望值。如果您添加一个最小复制场景,我们可以更好地了解您的问题。
RSpec.describe SomeClass do
  let(:thing) {
    described_class.new
  }
  
  describe '#some_method' do    
    it 'shows its policy' do
      expect(thing.policy).to receive(:show?).and_call_original
      thing.some_method
    end
  end
end