Cucumber步骤定义中的实例变量(即什么是self)有什么对象?

我不理解在Ruby中使用Cucumber时的范围,特别是关于实例变量。

对于我的直接示例的上下文,在hooks.rb的Before部分中, hooks.rb了变量@browser

@browser = Watir::Browser.new @browser_selected.to_sym

(其中@browser_selected通常是’chrome’)

在步骤定义中使用@browser。 举个简单的例子: @browser.send_keys(:tab)

我不明白的是什么对象包含@browser作为属性。 它在这方面有什么意义? 我知道我困惑的代码总是在一个块中,并且我认识到每个这样的块被使用(通过它附加的Given / When / Then消息)以某种神秘的方式进行预处理。

这种神秘的遮蔽是实例变量的范围。 如何知道这些块中的实例变量的范围?

self在Cucumber步骤和钩子只是一个Ruby对象,“世界”,在每个场景中使用。 每个步骤定义中的块都在世界的上下文中使用the_world.instance_eval或类似的东西执行,这意味着当每个块运行时, self就是世界。 因此,所有这些实例变量所属的对象是同一个对象,即世界。 所有这些实例变量的范围都是整个场景。

因此,在Cucumber步骤中谨慎使用实例变量非常重要,并在步骤名称中明确表示您正在使用它们(也就是说,在步骤名称中明确指出它们指的是某些状态)。 这些步骤清楚地指的是在幕后保存的东西(即引用相同的实例变量):

 Given there is a thing When I frob the thing Then the thing should be frobbed 

那很好也很正常。 但是如果When I frob the thing预先计算出一些预期的断言结果并将它们隐藏在实例变量中时,那将是非常可怕的, Then the thing should be frobbed在其断言中使用那些实例变量。 Then the thing should be frobbed不会起作用,除非When I frob the thing把它之前When I frob the thing了,使它不那么可重复使用,并且这种限制对于其他人写作function并不明显,他们会变得沮丧。 (不要像我以前的同事一样。)

回到世界:Cucumber为每个场景创造了一个新世界,并在最后抛出它,因此场景的实例变量不会影响下一个场景。 在普通的Cucumber中,世界只是Object一个实例。 在cucumber-rails中,它是Cucumber::Rails::World的一个实例( 有趣的是阅读 )。 除了在cucumber-rails中为世界构建的方法之外,世界通过扩展模块获得其方法(如在the_world.extend SomeModule )。 与实例变量一样,来自世界扩展的所有模块的所有方法都被卡在同一个对象(世界)上,因此您有时需要担心名称冲突。