【问题标题】:Stubbing a method call that accepts a block in rspec存根接受 rspec 中块的方法调用
【发布时间】:2015-03-16 08:47:17
【问题描述】:

我有一个这样的 lib 方法:

  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
  end

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

【问题讨论】:

    标签: ruby-on-rails unit-testing rspec


    【解决方案1】:

    如果使用rspec-mocks,您可以使用partial test double 存根对MyGem.search 的方法调用。

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

    MyGem 部分加倍并期望调用其search 类方法:

    expect(MyGem).to receive(:search)
    

    然后你可以在这个双重上添加额外的行为到check the call arguments,使它成为yield values,或者raise an error

    # 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")
    

    根据您的信息(似乎不完整: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
    

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

    RSpec.configure do |config|
      config.mock_with :rspec do |mocks|
        mocks.verify_partial_doubles = true
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2013-08-22
      • 2020-08-05
      • 2018-09-15
      • 1970-01-01
      • 2017-01-21
      • 1970-01-01
      • 1970-01-01
      • 2014-08-16
      • 2016-03-03
      相关资源
      最近更新 更多