Kuil Logoto
Kuil Logoto

Reputation: 11

Globally Access Variables for All Cucumber Steps from inside Hooks

currently I am loading all YAML files inside Cucumber Hooks like this before- the intention is to ensure we dont code individually for loading YAML files.

@all_Yaml_Files = Dir.entries(@projectData_Path).select {|f| !File.directory? f};
@all_Yaml_Files.each do |file_Name|
    file_Full_Path = @projectData_Path + '/' + file_Name
    instance_variable_set("@" + file_Name.split('.')[0], YAML.load_file(file_Full_Path))
end

However this code is inside a Before Cucumber Hook. So When the instance variables are loaded they are not available for next scenario. How can I make them run only once and then be available for all scenarios and all features - ie globally. Currently its loading everytime for a scenario.

Before do
....
end

Note: By doing above , we can directly access a YAML_FileName by @YAML_FileName variable without writing code. Thats my intention. Same happens with all excel files, json files etc etc. They are all called from inside Hooks.

Upvotes: 1

Views: 3477

Answers (1)

AnoE
AnoE

Reputation: 8345

EDIT: added the "quick" solution

Quick solution

You are using class variables (@var) now; these are subject to some Cucumber magic so they are cleaned out after each scenario, as you noticed. This makes them rather useful as well, giving you a "clean slate" while allowing you to transfer some state from one step to the next.

This is all just Ruby, so you can of course use global ruby variables ($var) instead. This still requires you to change your existing code.

If you want to keep your class variables @var and still have them to stick around, you could do hooks to save and restore them in globals, i.e.:

Around do |scenario, block|
  @var = $var
  begin
    block.call
  ensure
    $var = @var
  end
end

You'll want to make sure this hook is encountered early enough so it fires before any other hooks you might have.

I don't like to use globals in any language, so I try not to use either @var or $var in Cucumber. Instead, I prefer to do it the OOP way... hence:

"Clean" solution (PORO)

I use a singleton module for such things:

# support/000_cucu_globals.rb

class CucuGlobals
  include Singleton

  def initialize 
    @somevalue = 'hello world'
    ...
  end

  attr_reader :somevalue
end

Elsewhere:

puts CucuGlobals.instance.somevalue + "!"

Adjust/extend/get creative, as necessary. If you want to avoid typing so much, feel free to use a shorter class name (like G for global). You can also make your accessors class methods instead to even skip the instance in between. This is all just Ruby (not Cucumber) at this point, so there is not Cucumber magic involved anymore.

Hint: you want to load this module pretty early. I'm sure there is a good way to do that "cleanly", but I opted to just name it in such a way that is found first, by Cucumber (000_...).

Upvotes: 1

Related Questions