【问题标题】:How to write a unit test for method that yields an object to the block in RSpec如何为 RSpec 中的块生成对象的方法编写单元测试
【发布时间】:2020-02-01 19:27:30
【问题描述】:

我有一个实现.call 方法的类,该方法为块生成一个对象,我想学习如何为此编写单元测试。这就是我所拥有的。

module A
  class B < Service
    def call(object_id:)
      @object = Something.find(object_id)
      @object.update(status: 'done')
      yield @object
    end

    def set_to_in_progress
      @object.update(status: 'in_progress')
    end
  end
end

class Service
  def self.call(*args); new.call(*args); end
end

然后我这样使用它:

A::B.call(obj) do |object|
  object.set_to_in_progress if some_other_condition?
end

我需要能够为call 方法编写单元测试,以测试状态是否已更改为完成或进行中。这是我所拥有的:

RSpec.describe A::B, :unit do
  let(:object) { create(:something, id: 1, status: 'in_progress') }

  it 'updates the status to done' do
    described_class.call(object.id) do |???|
      ???
    end

    expect(object.status).to equal('done')
  end

  it 'updates the status to in progress' do
    described_class.call(object.id) do |???|
      ???
    end

    expect(object.status).to equal('in_progress')
  end
end

【问题讨论】:

  • 这个规范会不会失败,因为Something.find(object_id) 将找不到记录,因为您从未在规范中创建记录?
  • @Kris 你说得对,好地方!我已经修好了。

标签: ruby rspec rspec-rails rspec3


【解决方案1】:

你可以返回#call中的对象:

def call(object_id:)
  @object = Something.find(object_id)
  @object.update(status: 'done')
  yield @object
  @object
end    

那么在测试中你可以这样做:

object = described_class.call(object.id)
expect(object.status).to eq('in_progress')

【讨论】:

    【解决方案2】:

    您正在寻找yield_with_args matcher:

    specify do
      expect { |block| described_class.call(object.id, &block) }
        .to yield_with_args(object)
    end
    

    请注意,您正在混合测试行为(产生 object)和副作用(object 已更改)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-17
      • 2022-08-11
      • 1970-01-01
      相关资源
      最近更新 更多