【问题标题】:Test obj.extend(Foo) with RSpec doubles使用 RSpec 双打测试 obj.extend(Foo)
【发布时间】: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 不能通过扩展其实例来更改,这是预期的行为吗?

如果是这样,您如何为此处描述的示例创建可读规范?

链接

【问题讨论】:

  • 为什么不只是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的接口呢?

标签: ruby rspec mocking


【解决方案1】:

如果规范知道它expexts一个extendend对象,为什么不期待extend自己调用呢?

expect(obj).to receive(:extend).and_return { class.new.extend(...) }

【讨论】:

  • 我考虑了extend 的实现细节。可以通过以下方式实现相同的行为:def obj.foo; "extended-#{super}"; end 通过在此处更改实现,规范会中断:(
猜你喜欢
  • 1970-01-01
  • 2018-02-21
  • 2014-08-16
  • 2017-06-28
  • 2011-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多