How to pass objects with stubbed methods in it_behaves_like to shared_examples_for block in rspec?

∥☆過路亽.° 提交于 2020-01-04 13:49:37

问题


I have a spec like

describe MyClass do
  it_behaves_like SharedClass, MyClass.new
end

and in my shared example spec, I have

shared_examples_for SharedClass do |instance|
  before do 
    instance.some_my_class_method = double
  end
  # some specs here
end

There are few methods in MyClass instance that I cannot stub in the shared_examples_for block, so I want to stub them before passing it in it_behaves_like statement. like,

describe MyClass do
  before do
    @instance = MyClass.new
    @instance.stub(:my_class_method)
  end
  it_behaves_like SharedClass, @instance
end

but I cannot do that.It throws me
NoMethodError: undefined method `some_my_class_method=' for nil:NilClass

somehow I am not able to access that @instance object in the shared_examples_for context. I am using ruby 1.9.3p392 and rspec (2.14.1)


回答1:


The problem is that the arguments you pass to it_behaves_like are evaluated at the time it_behaves_like is called, while the before block is evaluated just before the shared example is executed, which occurs at a later time. As a result, @instance as an argument is evaluated to nil and the formal parameter instance is assigned nil, resulting in your undefined method error.

I don't know of any easy way to do what you're trying to accomplish. One alternative shown in this documentation is to eliminate the formal parameter and require the caller to pass a block which uses let to establish the argument value, using some agreed to variable, as follows:

describe MyClass do
  it_behaves_like SharedClass {let(:instance) { MyClass.new }}
end

describe MyClass do
  it_behaves_like SharedClass do
    let(:instance) do
      myClass = MyClass.new
      myClass.stub(:my_class_method)
      myClass
    end
  end
end

In addition to being more verbose, however, this has the disadvantage that there is no implicit documentation within your shared example that it expects instance to set when it's called.

Although I've not seen it discussed anywhere, a somewhat hacky alternative would be have your shared example use the agreed-to-variable from the block if and only if the value of the formal parameter is nil, which could be defaulted for convenience. Unfortunately, you can't use the same variable for both, as the value of the parameter will always take precedence.



来源:https://stackoverflow.com/questions/20899204/how-to-pass-objects-with-stubbed-methods-in-it-behaves-like-to-shared-examples-f

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!