我想为这种形式的代码编写一个单元测试:
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
但我特别好奇如何处理安全导航运算符。很多谷歌搜索已经出现了关于这个话题的zippo。
有什么想法吗?
不是最干净的方法,但在测试前启用对 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
是否在命令时被调用,这意味着它会改变系统状态并因此具有副作用。如果它只是访问一个变量,或者计算一个值,因此没有副作用,我根本不会费心测试调用,而是专注于测试被测方法的输入和输出。