如何使用RSpec通过Ruby安全导航&测试代码。操作员?

时间:2019-01-31 22:11:48

标签: ruby rspec

我想为这种形式的代码编写单元测试:

class Foo
  def bar(obj)
    return nil unless obj&.foo
    obj.bar
  end
end

这是示例测试:

RSpec.describe Foo do
  subject { Foo.new.bar(obj) }

  context 'when obj is nil' do
    let(:obj) { nil }

    it 'does not call :foo' do
      expect(obj).to_not receive(:foo)
      subject
    end
  end
end

此代码返回:

Failures:

  1) Foo when obj is nil does not call :foo
     Failure/Error: expect(obj).to_not receive(:foo)
       An expectation of `:foo` was set on `nil`. To allow expectations on `nil` and suppress this message, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `true`. To disallow expectations on `nil`, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `false`

默认情况下,RSpec 3.x在nil上设置期望值时会抱怨。我的代码库很大,因此无法修改此全局设置来简化这种代码的测试。全球变化可能会给其他开发人员带来麻烦。

我知道我可以将代码修改为return nil unless obj.present? && obj.foo之类的东西,但是我特别想知道如何处理安全的导航操作符。在Google上进行了大量的谷歌搜索工作。

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

并不是最干净的方法,但是至少要在测试之前启用对nil的期望并在之后禁用它。因此,将规格更改为

RSpec.describe Foo do
  subject { Foo.new.bar(obj) }

  context 'when obj is nil' do
    let(:obj) { nil }

    before do 
      RSpec::Mocks.configuration.allow_message_expectations_on_nil = true
    end

    after do 
      RSpec::Mocks.configuration.allow_message_expectations_on_nil = false
    end

    it 'does not call :foo' do
      expect(obj).to_not receive(:foo)
      subject
    end
  end
end

如果是重复出现的模式,也可以将其放在rspec宏(context 'when obj is nil', nil_mock: true do)内的代码块之前和之后。

但是,我不确定是否值得,因为弄乱这些全局设置会产生副作用,这可能会导致某些依赖于顺序的错误,这些错误以后很难调试。另外,我实际上仅测试foo是一条命令时是否已被调用,这意味着它会更改系统状态,因此会产生副作用。如果它只是访问一个变量或计算一个值而没有任何副作用,那么我根本就不会去测试调用,而是专注于测试被测方法的输入和输出。