Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Namespaced worlds become nil after first scenario runs #1595

Closed
mitchgrout opened this issue Dec 8, 2021 · 1 comment · Fixed by #1603
Closed

Namespaced worlds become nil after first scenario runs #1595

mitchgrout opened this issue Dec 8, 2021 · 1 comment · Fixed by #1603
Labels
🐛 bug Defect / Bug

Comments

@mitchgrout
Copy link

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:

  1. 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
  1. 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
  1. Run the tests, and observe the results for the following scenarios:
    • 1. All checks pass passes
    • 2. Namespaced world becomes nil fails with expected not #<NilClass:8> => nil got #<NilClass:8> => nil
    • 3. Stateful methods do not work fails with expected 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.

@aurelien-reeves
Copy link
Contributor

Nice catch, and thanks for the detailed report! 🤩

@aurelien-reeves aurelien-reeves added the 🐛 bug Defect / Bug label Dec 13, 2021
aurelien-reeves added a commit that referenced this issue Jan 5, 2022
* Add a reproducible example for #1595

* Make sure module is valid before extending the world with it

* Update CHANGELOG.md

* Simplify world.feature

* Extend the world only if modules.any?
aurelien-reeves added a commit that referenced this issue Jan 7, 2022
…assed (#1606)

* Add a reproducible example for #1595

* Make sure module is valid before extending the world with it

* Update CHANGELOG.md

* Simplify world.feature

* Extend the world only if modules.any?

* Add reproducible example

* Add status of the run when emitting TestRunFinished

* Fix TestRunFinished.success message in the message builder

* Update CHANGELOG.md

* Update example to remove failing step actually not failing

* Add newline at the end of the feature file

* Make Runtime#failure? a real public method
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Defect / Bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants