我想为这种形式的代码编写单元测试:
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上进行了大量的谷歌搜索工作。
有什么想法吗?
答案 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
是一条命令时是否已被调用,这意味着它会更改系统状态,因此会产生副作用。如果它只是访问一个变量或计算一个值而没有任何副作用,那么我根本就不会去测试调用,而是专注于测试被测方法的输入和输出。