在函数内创建RSpec上下文

G G*_*III 7 ruby rspec ruby-on-rails

为了避免在我的Rspec测试中重复自己,我想编写一个这样的函数

def with_each_partner(&block)
  PARTNER_LIST.each do |partner|
    context "with partner #{partner.name}" { yield partner }
  end
end
Run Code Online (Sandbox Code Playgroud)

我有这样一个功能,它的工作原理是所有测试都使用正确的值提供给合作伙伴,但是在输出过程中它们不会作为"with partner X"上下文的一部分打印:相反,如果我有一个测试像这样:

describe Thing do
  subject { Thing.new(partner) }
  with_each_partner do |partner|
    it 'does its thing' do
      expect(subject.do).to eq 'its thing'
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我最终得到这样的输出:

Thing
  does its thing
Run Code Online (Sandbox Code Playgroud)

而不是所需的输出,如:

Thing
  with partner X
    does its thing
  with partner Y
    does its thing
Run Code Online (Sandbox Code Playgroud)

如何让RSpec正确使用我函数中创建的上下文?

Myr*_*ton 9

TL; DR:这样做:

def with_each_partner(&block)
  PARTNER_LIST.each do |partner|
    context "with partner #{partner.name}" do
      class_exec(&block)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

说明

RSpec的DSL通过评估带有变化的块来工作self- 这是it一个describe或一个context块内的方法,但不在它之外.当你yield,所提供的块与原始评价self,这是self在被限定的块的点.这意味着使用您的原始with_each_partner定义,此代码:

describe Thing do
  subject { Thing.new(partner) }
  with_each_partner do |partner|
    it 'does its thing' do
      expect(subject.do).to eq 'its thing'
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

真的被评估如下:

describe Thing do
  subject { Thing.new(partner) }
  outer_example_group = self
  with_each_partner do |partner|
    outer_example_group.it 'does its thing' do
      expect(subject.do).to eq 'its thing'
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

...因此,单个示例在外部示例组上定义,而不是在"with partner #{partner.name}"嵌套组上定义.

class_exec 在类/模块的上下文中计算提供的块.在这种情况下,该类是RSpec为您的上下文生成的示例组子类.使用class_exec确保it调用when时,接收器是嵌套context示例组而不是外部示例组,从而创建所需的结果.

  • 呵呵。嗯...我相信这位先生对 [rspec](https://github.com/rspec/rspec) 有一定的了解。 (2认同)