I'm not understanding scoping when using Cucumber in Ruby, especially with regard to instance variables.
For the context of my immediate example, in the Before portion of
@browser = Watir::Browser.new @browser_selected.to_sym
self in Cucumber steps and hooks is just a Ruby object, the "world", which is used throughout each scenario. The block in each step definition is executed in the context of the world with
the_world.instance_eval or something similar, meaning that when each block runs
self is the world. So the object to which all of those instance variables belong is the same object, the world. The scope of all of those instance variables is the entire scenario.
So it's important to use instance variables sparingly in Cucumber steps, and to make it clear in step names that you're using them (that is, make it clear in step names that they refer to some piece of state). These steps clearly refer to a thing which is saved behind the scenes (i.e. refer to the same instance variable):
Given there is a thing When I frob the thing Then the thing should be frobbed
That's fine and normal. But it would be terrible if
When I frob the thing precalculated some expected assertion results and stashed them in instance variables too, and
Then the thing should be frobbed used those instance variables in its assertions.
Then the thing should be frobbed would not work unless
When I frob the thing preceded it, making it less reusable, and that restriction would not be obvious to others writing features and they would become frustrated. (Don't be like my former colleague.)
Back to the world: Cucumber makes a new world for each scenario and throws it away at the end so a scenario's instance variables don't affect the next scenario. In plain Cucumber, the world is just an instance of
Object. In cucumber-rails, it's an instance of
Cucumber::Rails::World (which is interesting to read). Aside from the methods built in to the world in cucumber-rails, the world gets its methods by extending modules (as in
the_world.extend SomeModule). As with instance variables, all the methods from all the modules that the world extends are jammed on to the same object (the world), so you sometimes need to worry about name conflicts.