存根接受 rspec 中的块的方法调用

ven*_*tKA 4 unit-testing rspec ruby-on-rails

我有一个像这样的 lib 方法:

  def search(*args, &blk)
    MyGem.search(*args, &blk)
  end
Run Code Online (Sandbox Code Playgroud)

我有另一种调用此搜索方法的方法,如下所示:

  def members_of(dept)
    result = {}
    search(:attributes => ["displayname", "employeeID"]) do |entry|
      result[entry.employeeid.first.to_i] = entry.displayname.first.to_s
    end
    result
  end
Run Code Online (Sandbox Code Playgroud)

当我为 编写 rspec 测试时members_of,我试图对search调用进行存根。但我无法弄清楚如何准确地存根该块,因为该块实际上正在使用该方法中定义的局部变量之一。任何帮助,将不胜感激。谢谢。

cbl*_*ard 5

如果使用rspec-mocksMyGem.search ,您可以使用部分 test double来存根方法调用。

部分测试替身是系统中真实对象的扩展,该系统在测试上下文中具有类似测试替身的行为。这种技术在 Ruby 中非常常见,因为我们经常看到类对象充当方法的全局命名空间。

要进行部分双精度MyGem并期望调用其search类方法:

expect(MyGem).to receive(:search)
Run Code Online (Sandbox Code Playgroud)

然后,您可以在此 double 上添加其他行为来检查调用参数、使其产生值或引发错误:

# match arguments
expect(MyGem).to receive(:search).with(:attributes => ["displayname", "employeeID"])
# yield value
expect(MyGem).to receive(:search).and_yield(42)
# yield multiple values
expect(MyGem).to receive(:search).and_yield(42).and_yield(987)
# raise an error
expect(MyGem).to receive(:search).and_raise(ArgumentError, "bad request")
# all at once!
expect(MyGem).to receive(:search).with(:attributes => ["displayname", "employeeID"])
    .and_yield(42)
    .and_raise(ArgumentError, "bad request")
Run Code Online (Sandbox Code Playgroud)

根据您的信息(似乎不完整:dept从未使用过参数...),这是一个完整的示例:

Entry = Struct.new(:employeeid, :displayname)

class MyGem
  def self.search(*args)
    yield Entry.new([1, 2, 3], ["one", "two", "three"])
  end
end

class Organization
  def search(*args, &blk)
    MyGem.search(*args, &blk)
  end

  def members_of(dept)
    result = {}
    search(:attributes => ["displayname", "employeeID"]) do |entry|
      result[entry.employeeid.first.to_i] = entry.displayname.first.to_s
    end
    result
  rescue
    []
  end
end

RSpec.describe Organization do
  describe "#members_of" do
    it "calls search" do
      organization = Organization.new
      expect(MyGem).to receive(:search).with(:attributes => ["displayname", "employeeID"])
          .and_yield(Entry.new([1337, 100, 2000], ["leet", "one hundred", "two thousand"]))
          .and_yield(Entry.new([123], ["one-two-three"]))
      members = organization.members_of("dept")
      expect(members).to match({
          1337 => "leet",
          123 => "one-two-three"})
    end
    it "returns empty array on search error" do
      organization = Organization.new
      expect(MyGem).to receive(:search).and_raise(ArgumentError)
      expect(organization.members_of("dept")).to eq([])
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

另外,当使用部分双精度时,您应该启用verify_partial_doubles配置选项以确保您不会存根不存在的类方法。使用文件中的以下代码片段启用它spec/spec_helper.rb:

RSpec.configure do |config|
  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end
end
Run Code Online (Sandbox Code Playgroud)