【发布时间】:2015-11-30 11:14:44
【问题描述】:
RSpec double 不能通过扩展其实例来更改。
小例子
请注意,此处描述的示例只是一个最小示例
来证明问题。原来的类更复杂
需要指定的行为(obj.extend(Something))并且
无法更改。
规格
让我们先看一下规范。 在下面的示例中,您可以看到规范的外观:
require 'spec_helper'
RSpec.describe Modifier do
subject(:modifier) { described_class.new(active) }
describe "#apply_to(obj)" do
subject(:obj) { instance_double(Foo, foo: "foo") }
before { modifier.apply_to(obj) }
context "when active" do
let(:active) { true }
its(:foo) { is_expected.to eq("extended-foo") } # NOTE: This one will fail
end
context "when not active" do
let(:active) { false }
its(:foo) { is_expected.to eq("foo") }
end
end
end
不幸的是,这不起作用:(
Failures:
1) Modifier#apply_to(obj) when active foo should eq "extended-foo"
Failure/Error: its(:foo) { is_expected.to eq("extended-foo") }
expected: "extended-foo"
got: "foo"
富
class Foo
attr_reader :foo
def initialize(foo)
@foo = foo
end
end
修饰符
这个类将通过扩展它的实例方法来修改给定的对象。
class Modifier
def initialize(active)
@active = active
end
# NOTE: This is the interesting method
def apply_to(obj)
return unless active?
obj.extend(Extended) # NOTE: And this is the interesting LOC
end
def active?
@active
end
private
attr_reader :active
module Extended
def foo
"extended-#{super}"
end
end
end
更简单的例子
整个问题可以分解为如下代码sn-p:
module Bar
def foo
"bar"
end
end
double = RSpec::Mocks::Double.new("Foo", foo: "foo")
obj = Object.new
double.extend(Bar)
obj.extend(Bar)
double.foo
# => "foo"
obj.foo
# => "bar"
结论
RSpec doubles 不能通过扩展其实例来更改,这是预期的行为吗?
如果是这样,您如何为此处描述的示例创建可读规范?
链接
- 回购复制:Extend RSpec doubles (Example)
- RSpec:Using an instance double
- GitHub:RSpec Issue 1100
【问题讨论】:
-
为什么不只是
subject(:obj) { Foo.new('foo') }? -
因为这是一个简化的例子。实际上,创建此对象的成本很高。它看起来更像:
Foo.new(lot, of, complex, arguments) 有很多依赖项。这就是为什么我想模拟Foo。 -
你可以通过
Class.new { ... }创建一个未命名的类,而不是模拟真实的Foo -
@Stefan 这正是我现在要采用的解决方案。不幸的是,我们将失去验证双打的能力。 (
instance_double将验证您模拟的接口是否存在。因此您不能存根不存在的方法。例如:instance_double(Foo, bar: "bar")将失败。) -
但是您正在测试
Modifier,而不是Foo。为什么一定要验证Foo的接口呢?