将信息从“之前”块传递到“之后”块

问题描述 投票:0回答:1

(我正在使用 JRuby,但我认为我的问题也适用于 MRI Ruby)

我的 RSpec 定义具有以下总体结构:

RSpec.describe 'XXX' do
  before(:all) do
    # preparation common for each example
  end
  after(:all) do
    # clean up after each example
  end
  context 'C1' do
    it 'Ex1' do
      ....
    end
    it 'Ex2' do
      ....
    end
    ....
 end
 context 'C2' do 
   .... # more examples here
 end

结束

这里的问题是准备工作(

before(:all)
)涉及计算一个值,比如说

some_value=MyMod.foo()

我在清理代码中需要它(

after(:all)
),即

MyMod.bar(some_value)

当然,由于

some_value
是一个局部变量,我不能这样做。理论上,我可以使用全局变量,

 before(:all) do
   $some_value=MyMod.foo()
 end
 after(:all) do
   MyMod.bar($some_value)  
 end

但这不仅丑陋,如果我决定有一天并行化我的测试,还会导致问题。

这能实现吗?还是我的整体设计有缺陷?

ruby rspec jruby
1个回答
0
投票

正如 Stefan 在评论中已经指出的,您可以为此使用实例变量。

具体来说,每个 RSpec example group (即

describe
context
块)定义一个类,以及任何示例(
it
describe
等)和钩子(
before
after
和组中的
around
)在该类实例的上下文中进行评估。

因此,如文档所示,您可以在

before
块中定义实例变量(将属于示例组类的该实例),并从
it
after
块中访问它同一组(或在嵌套组中,它们从父组继承钩子),例如像这样:

describe 'something' do
  before(:each) do
    @some_value = MyMod.foo()
  end

  after(:each) do
    MyMod.bar(@some_value)  
  end

  # Add your examples here...
end

(您还可以在此处使用

before(:all)
after(:all)
,在这种情况下,钩子仅针对整个示例组执行一次,而不是在组中的每个示例之前和之后执行。除此之外,它们的工作原理完全相同但请注意,
before(:suite)
钩子不能用于定义这样的实例变量,因为它们的计算方式不同,一般情况下,我建议不要使用它们。)


另一种解决方案是使用

let!
定义一个 memoized 辅助方法(因为
!
)在任何示例之前自动评估,并且其保存的值稍后仍然可用:

describe 'something' do
  # This is evaluated in an implicit before hook before each example:
  let!(:some_value) { MyMod.foo() }

  after(:each) do
    MyMod.bar(some_value)  
  end

  # Add your examples here...
end

这种方法的一个优点(除了可以说是更干净的语法,更少的

@
符号)是
some_value
不能在示例中重新分配(这可能会导致微妙的错误)。 但是,它的 contents 当然可能仍然包含可变对象,因此这并不能完全保证
after
块中恢复的设置仍然与示例之前的设置相同。

此外,虽然您可以在单个

before
块中设置多个实例变量,但
let!
只允许您定义一个辅助方法。 当然,您始终可以通过以下方式解决该限制:

describe 'something' do
  let!(:saved_settings) do
    some_value = MyMod.foo()
    other_value = MyMod.foo2()
    [some_value, other_value].freeze
  end

  after(:each) do
    some_value, other_value = saved_settings
    MyMod.bar(some_value)
    MyMod.bar2(other_value)
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.