如何在rspec中模拟实例变量

Mik*_*ike 6 rspec

我有两个类OneClassAnotherClass:

class OneClass
  def initialize(*args)
    @another_member = AnotherClass.new()
  end

  def my_method()
    if @another_member.another_method1() then
      @another_member.another_method2()
    end
    @another_member.another_method3()
  end
end
Run Code Online (Sandbox Code Playgroud)

我正在写单位OneClass.我怎么能嘲笑@another_member

Sim*_*tti 5

您不能模拟实例变量。您只能模拟方法。一种选择是在内部定义一个方法OneClass来包装another_member,并模拟该方法。

class OneClass
  def initialize(*args)
  end

  def my_method()
    if another_member.another_method1() then
      another_member.another_method2()
    end
    another_member.another_method3()
  end

  private

  def another_member
    @another_member ||= AnotherClass.new()
  end

end
Run Code Online (Sandbox Code Playgroud)

但是,您不必这样做,有更好的方法来编写和测试您的代码。在这种情况下,更好的模拟方法是使用称为Dependency Injection的模式。

您将依赖项传递给初始化程序。

class OneClass
  def initialize(another: AnotherClass, whatever:, somethingelse:)
    @another_member = another.new()
  end

  def my_method()
    if @another_member.another_method1() then
      @another_member.another_method2()
    end
    @another_member.another_method3()
  end
end
Run Code Online (Sandbox Code Playgroud)

(注意我使用了关键字参数,但您不必这样做。您也可以使用标准的 args 方法)。

然后,在测试套件中,您只需提供测试对象。

let(:test_another) {
  Class.new do
    def another_method1
      :foo
    end
    def another_method2
      :bar
    end
    def another_method3
      :baz
    end
  end
}

it "does something" do
  subject = OneClass.new(another: test_another)
  # ...
end
Run Code Online (Sandbox Code Playgroud)

这种方法有几个优点。特别是,您避免在测试中使用模拟,而是真正独立地测试对象。


Mik*_*ike 5

有了安东尼的想法,我让它发挥作用。

describe OneClass do
  before(:each) { @one_object = OneClass.new }

  describe 'my_method' do
    it 'should work' do
      mock_member = double
      allow(mock_member).to receive(:another_method1).and_return(true)
      @one_object.instance_variable_set(:@another_member, mock_member)

      @one_object.my_method()

      expect(mock_member).to have_received(:another_method1)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)