RSpec: 在示例中从块内访问 `let` 定义

RSpec: Accessing a `let` definition from within a block in an example

我在 RSpec 测试中基本上有这个设置:

RSpec.describe Something do
  describe '#something' do
    let(:my_var) { 'some data' }
    let(:my_object) { MyObject.new }
    it 'example' do
      my_object.instance_eval { @my_inst_var = my_var } # ERROR here
    end
  end
end

这导致

NameError: #MyObject:0x5d14e99e

的未定义局部变量或方法“myvar”

我明白,在 my_object 的执行上下文中,Ruby 不知道如何处理 RSpec 中 let 构造的“魔法”。我知道我可以在这里使用“普通变量”而不是 let,我也可以这样做(这就是我目前解决问题的方式):

    # This works of course
    it 'example' do
      mv = my_var # Forces the let(:my_var) to be evaluated
      my_object.instance_eval { @my_inst_var = mv }
    end

问题:假设我确实想在示例中使用 let 和 `instance_eval,是否有更好的方法?

有多种方法可以做到这一点。使用 instance_eval 在我的列表中并不高。 如果 attr_accessor 有意义,您肯定应该这样做:

class Something
  attr_accessor :my_inst_var
end

it 'example' do
  my_object.my_inst_var = my_var
end

如果您不想污染您的对象 public 方法,我认为以下选项比 instance_eval.

更干净
it 'example' do
  my_object.instance_variable_set :@my_instance_var, my_var
end

改用instance_exec——它允许您将参数传递到块的范围内:

my_object.instance_exec(my_var) { |v| @my_inst_var = v }

或者,您可以通过 instance_variable_set:

设置实例变量
my_object.instance_variable_set(:@my_inst_var, my_var)

虽然以上两种方法都有效,但以这种方式改变对象的状态可能会导致脆弱的测试。您应该考虑更改对象,以便更容易测试。 (加一个setter或初始化时传值)