Namespaced worlds become nil after first scenario runs
mitchgrout opened this issue · comments
Describe the bug
When namespaced Worlds are used, after the first scenario runs all namespaced worlds change from being a valid Object
to being nil
. This has no impact on pure methods, but stateful methods (e.g. attr_accessor
) break in unclear ways. This is currently not caught by the worlds test suite (features/docs/writing_support_code/world.feature
), as it does not test stateful methods, nor multi-scenario features.
To Reproduce
Steps to reproduce the behavior:
- Create a step definition file containing the following definitions:
module ModuleOne
attr_accessor :state
def pure
42
end
end
World(module_one: ModuleOne)
Then /^module one is not nil$/ do
expect(module_one).not_to be nil
end
Then /^state can be set$/ do
expect { module_one.state = 42 }.not_to raise_error
end
Then /^pure method can be called$/ do
expect { module_one.pure }.not_to raise_error
end
- Create a feature file containing the following scenarios:
Feature: Multi-scenario namespaced worlds which keep state state
Scenario: 1. All checks pass
Then module one is not nil
Then state can be set
Then pure method can be called
Scenario: 2. Namespaced world becomes nil
Then module one is not nil
Scenario: 3. Stateful methods do not work
Then state can be set
Scenario: 4. Pure methods still work
Then pure method can be called
- Run the tests, and observe the results for the following scenarios:
1. All checks pass
passes2. Namespaced world becomes nil
fails withexpected not #<NilClass:8> => nil got #<NilClass:8> => nil
3. Stateful methods do not work
fails withexpected no Exception, got #<FrozenError: can't modify frozen NilClass: nil>
4. Pure methods still work
passes
Expected behavior
It is expected that all scenarios pass, regardless of execution order, as well as that namespaced worlds can maintain state per-scenario, via standard Ruby patterns such as attr_accessor
.
Context & Motivation
Looking at using namespaced worlds to provide an up-front declaration of my test suites state, rather than relying on instance variables.
Screenshots
N/A
Your Environment
- ruby-cucumber, v7.1.0 & main (a7e6855)
- ruby, v3.0.0
- Ubuntu 20.04 LTS, running kernel 5.4.0
Additional context
This issue does not appear to be a problem with non-namespaced worlds, i.e. both scenarios would run if World(ModuleOne)
was used, and the module_one.
namespace dropped from the step definitions.
The behaviour can be resolved if all namespaced modules are explicitly undefined after each scenario (i.e. via an After
hook). This was traced back to lib/cucumber/glue/proto_world.rb
in add_namespaced_modules!
, lines 189 - 193:
inner_world = if self.class.respond_to?(namespace)
instance_variable_get(variable_name)
else
Object.new
end
It can also be resolved if the conditional is replaced with just Object.new
.
I believe that after the first scenario executes, the instance variable "@__#{namespace}_world"
is set to nil alongside all other global state, but is not reinitialized to a new valid Object. The modules methods are then defined on nil
, which causes stateful methods to fail since nil
is frozen.
Nice catch, and thanks for the detailed report! 🤩