如何用rspec中的一个参数和一个块测试一次方法调用

问题描述 投票:0回答:2
class ExternalObject
  attr_accessor :external_object_attribute

  def update_external_attribute(options = {})
    self.external_object_attribute = [1,nil].sample
  end
end

class A
  attr_reader :my_attr, :external_obj

  def initialize(external_obj)
    @external_obj = external_obj
  end

  def main_method(options = {})
    case options[:key]
    when :my_key
      self.my_private_method(:my_key) do 
        external_obj.update_external_attribute(reevaluate: true)
      end
    else
      nil
    end
  end

  private
  def my_private_method(key)
     old_value = key
     external_object.external_object_attribute = nil
     yield
     external_object.external_object_attribute = old_value if external_object.external_object_attribute.nil?
  end
end

我想在main_method时测试options[:key] == :my_key的以下内容:

[my_private_method用参数:my_key调用一次,它具有一个块{external_obj.update_external_attribute(reevaluate: true) },该块用参数update_external_attribute调用external_obj上的reevaluate: true一次。

我能够使用my_private_method参数测试一次:my_key调用。

expect(subject).to receive(:my_private_method).with(:my_key).once

但是我如何测试期望的其余部分?

谢谢

ruby-on-rails ruby rspec tdd rspec-rails
2个回答
0
投票

如果您也发布测试,可能会更容易回答您的问题。设置,执行和资产/期望。

您可以在this older question中找到简短的答案。

您会发现对阅读yield matchers很有用。

如果您还没有的话,我建议您嘲笑yield。但是,除非您发布实际的测试代码,否则我无法告诉您。


0
投票

我将回答您的问题。但是,接下来我将解释为什么您不应该那样做,并向您展示一种更好的方法。

在测试设置中,需要让double产生,这样代码才能进入您的代码块。

ExternalObject

行得通。测试通过。 RSpec是一个功能强大的工具。而且,它将使您摆脱困境。但是,这并不意味着您应该这样做。测试私有方法总是一个坏主意。

测试应仅测试类的公共接口。否则,您将陷入当前的实现中,从而在重构类的内部工作时导致测试失败-即使您没有更改对象的外部可见行为。

这是一种更好的方法:

RSpec.describe A do
  subject(:a) { described_class.new(external_obj) }

  let(:external_obj) { instance_double(ExternalObject) }

  describe '#main_method' do
    subject(:main_method) { a.main_method(options) }

    let(:options) { { key: :my_key } }

    before do
      allow(a).to receive(:my_private_method).and_yield
      allow(external_obj).to receive(:update_external_attribute)

      main_method
    end

    it 'does something useful' do
      expect(a)
        .to have_received(:my_private_method)
        .with(:my_key)
        .once

      expect(external_obj)
        .to have_received(:update_external_attribute)
        .with(reevaluate: true)
        .once
    end
  end
end

注意,关于私有方法的期望已经消失了。现在,测试仅依赖于RSpec.describe A do subject(:a) { described_class.new(external_obj) } let(:external_obj) { instance_double(ExternalObject) } describe '#main_method' do subject(:main_method) { a.main_method(options) } let(:options) { { key: :my_key } } before do allow(external_obj).to receive(:update_external_attribute) allow(external_obj).to receive(:external_object_attribute=) allow(external_obj).to receive(:external_object_attribute) main_method end it 'updates external attribute' do expect(external_obj) .to have_received(:update_external_attribute) .with(reevaluate: true) .once end end end class A的公共接口。

希望有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.