我想断言,某个方法被调用N次(不多也不少),并带有特定的参数和特定的顺序.我也不想实际执行这个方法,所以首先我用allow()作为存根.

假设我有这样的代码:

class Foo
  def self.hello_three_times
    Foo.hello(1)
    Foo.hello(2)
    Foo.hello(3)
  end

  def self.hello(arg)
    puts "hello #{arg}"
  end
end

我想测试方法hello_three_times,它使用1、2和3作为参数调用hello三次.(我不想在测试中真正调用hello,因为实际上它包含副作用并且速度很慢.)

所以,如果我写这个测试

RSpec.describe Foo do
  subject { Foo.hello_three_times }

  it do
    allow(Foo).to receive(:hello).and_return(true)
    expect(Foo).to receive(:hello).with(1).once.ordered
    expect(Foo).to receive(:hello).with(2).once.ordered
    expect(Foo).to receive(:hello).with(3).once.ordered
    subject
  end
end

它通过了,但不能保证之后不会有额外的调用.例如,如果有一个错误,方法hello_three_times实际上看起来像这样

def self.hello_three_times
  Foo.hello(1)
  Foo.hello(2)
  Foo.hello(3)
  Foo.hello(4)
end

测试仍将是绿色的.

如果我试着把它和像这样的exactly(3).times结合起来

RSpec.describe Foo do
  subject { Foo.hello_three_times }

  it do
    allow(Foo).to receive(:hello).and_return(true)
    expect(Foo).to receive(:hello).exactly(3).times
    expect(Foo).to receive(:hello).with(1).once.ordered
    expect(Foo).to receive(:hello).with(2).once.ordered
    expect(Foo).to receive(:hello).with(3).once.ordered
    subject
  end
end

它失败是因为RSpec似乎在第一个Expect之后将调用视为已完成(可能在本例中,它的工作方式是预期首先有3个调用,然后再分别有3个调用,因此总共有6个调用):

Failures:
  1) Foo is expected to receive hello(3) 1 time
     Failure/Error: expect(Foo).to receive(:hello).with(1).once.ordered
       (Foo (class)).hello(1)
           expected: 1 time with arguments: (1)
           received: 0 times

有没有一种方法可以组合这样的预期,从而保证恰好有3个参数为1、2和3(有序的)的调用(不多也不少)?

推荐答案

哦,我想我找到解决办法了.我可以使用块来实现这一点:

RSpec.describe Foo do
  subject { Foo.hello_three_times }

  let(:expected_arguments) do
    [1, 2, 3]
  end

  it do
    allow(Foo).to receive(:hello).and_return(true)
    call_index = 0
    expect(Foo).to receive(:hello).exactly(3).times do |argument|
      expect(argument).to eq expected_arguments[call_index]
      call_index += 1
    end
    subject
  end
end

它完成了这项工作,确保了恰好有3个带有正确参数的调用.

但是它看起来并不是很漂亮(引入了局部变量call_index,呃).也许有更好的解决方案呢?

Ruby相关问答推荐

是否可以在 Ruby 中使用正则表达式匹配字符串\b(退格字符)?

Jekyll 默认安装没有 _layouts 目录

Ruby 中的关联数组

卸载所有 gem Ruby 2.0.0

Ruby 中的 method_missing trap

在 Ruby 中重写 to_s 方法不好吗?

从命令行使用Bundle 器将 gem 添加到 gemfile

如何在不等式中使用Ruby case ... when?

如何测试数组中的所有项目是否相同?

Ruby:将字符串转换为日期

是什么让 Ruby 变慢了?

跳过 Enumerable#collect 中的迭代

处理来自 Net::HTTP 的异常的最佳方法是什么?

如何在 Ruby 中取消定义类?

Ruby 方法to_sym有什么作用?

Ruby 中的=~运算符是什么?

Ruby 将 CSV 文件读取为 UTF-8 和/或将 ASCII-8Bit 编码转换为 UTF-8

为什么表达式 (true == true == true) 会产生语法错误?

不能在windows上安装thin

Ruby:如何计算一个字符串在另一个字符串中出现的次数?