How to define a simple global variable in an rspec test that can be accesed by helper functions - ruby

I cant figure out how to use a simple global variable in an rspec test. It seems like such a trivial feature but after much goggleing I havent been able to find a solution.
I want a variable that can be accessed/changed throughout the main spec file and from functions in helper spec files.
Here is what I have so far:
require_relative 'spec_helper.rb'
require_relative 'helpers.rb'
let(:concept0) { '' }
describe 'ICE Testing' do
describe 'step1' do
it "Populates suggestions correctly" do
concept0 = "tg"
selectConcept() #in helper file. Sets concept0 to "First Concept"
puts concept0 #echos tg?? Should echo "First Concept"
end
end
.
#helpers.rb
def selectConcept
concept0 = "First Concept"
end
Can someone point out what I am missing or if using "let" is totally the wrong method?

Consider using a global before hook with an instance variable: http://www.rubydoc.info/github/rspec/rspec-core/RSpec/Core/Configuration
In your spec_helper.rb file:
RSpec.configure do |config|
config.before(:example) { #concept0 = 'value' }
end
Then #concept0 will be set in your examples (my_example_spec.rb):
RSpec.describe MyExample do
it { expect(#concept0).to eql('value') } # This code will pass
end

It turns out the easiest way is to use a $ sign to indicate a global variable.
See Preserve variable in cucumber?

This is an old thread, but i had this question today. I just needed to define a long string to stub out a command that is in multiple files as:
# in each spec file that needed it
let(:date_check) do
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in that file (or a shared context)
before(:each) do
stub_command(date_check).and_return(false)
end
Searched, Stack Overflow, etc, landed on this: Note the usage of the variable doesn't change at all! (Assumes all specs require 'spec_helper')
# in spec_helper.rb
def date_check
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in any spec file
before(:each) do
stub_command(date_check).and_return(false)
end

I suggest you define the variable in the helper file, where it can be used by other helper code, and can be accessed from your tests.
For my project, I wanted to keep all the setup stuff in spec_helper.rb, and use those settings, plus any custom variables and methods in the tests. The following, modified from the RSpec-core 3.10 docs, is not Rails-specific.
Create a new setting for RSpec.configure called my_variable, and give it a value, like this:
# spec/spec_helper.rb
RSpec.configure do |config|
config.add_setting :my_variable
config.my_variable = "Value of my_variable"
end
Access settings as a new read-only property in RSpec.configuration from your test:
# spec/my_spec.rb
RSpec.describe(MyModule) do
it "creates an instance of something" do
my_instance = MyModule::MyClass.new(RSpec.configuration.my_variable)
end
end

Related

How can I skip some setup for specific Rspec tags?

Rspec makes it easy to configure setup based on the presence of test tags. For example, if some tests need a parallel universe to be created (assuming you have code to do that):
# some_spec.rb
describe "in a parallel universe", alter_spacetime: true do
# whatever
end
# spec_helper.rb
RSpec.configure do |config|
config.before(:each, :alter_spacetime) do |example|
# fancy magic here
end
end
But I want to do the opposite: "before each test, unless you see this tag, do the following..."
How can I skip a setup step in spec_helper based on the presence of a tag on some tests?
At first, you would expect something like
RSpec.configure do |config|
config.before(:each, alter_spacetime: false) do |example|
# fancy magic here
end
end
to work that way, but it doesn't.
But you have access to example, which is an Example instance and has the #metadata method, which returns Metadata object. You can check the value of the flag with that, and a flag on the specific example will override one on the containing describe block.
config.before(:each) do |example|
# Note that we're not using a block param to get `example`
unless example.metadata[:alter_spacetime] == false
# fancy magic here
end
end

File.read empty for a non empty file when testing with rspec

New to rubby and rspec i am trying to test a class that opens and write to a file.
The class name is SimpleLogger
Here is the spec that generates an error:
describe SimpleLogger do
...
context 'when using a file' do
require 'fakefs/spec_helpers'
before(:all) do
#path = 'my_file'
logger = SimpleLogger.new #path
logger.write "Hello, world!"
logger.close
end
...
it 'we expect the file to have a valid content' do
expect(File.read(#path)).to eq "Hello, world!\n"
end
end
end
The error generated is:
Failure/Error: expect(File.read(#path)).to eq "Hello, world!\n"
expected: "Hello, world!\n"
got: ""
(compared using ==)
Diff:
## -1,2 +1 ##
-Hello, world!
The file exists on my file system, and when I'm testing a simple puts Find.read("my_file") on an independant ruby file i've got the expected result.
I've tested and have the same issue without the fakefs gem
Why is it when run in a spec it doesn't work?
And beside that i fail to understand the advantage of fakefs, as it creates the file juste the same. So why fakefs is used?
And as it creates the file should i erase it within the spec?
Thanks in advance ;)
From the documentation - it seems that you need to include the helpers to activate the FakeFS:
FakeFS::SpecHelpers provides a simple macro for RSpec example groups to turn FakeFS on and off.
To use it simply require 'fakefs/spec_helpers', then include FakeFS::SpecHelpers into any
example groups that you wish to use FakeFS in. For example:
require 'fakefs/spec_helpers'
describe "Some specs that deal with files" do
include FakeFS::SpecHelpers
...
end
By default, including FakeFS::SpecHelpers will run for each example inside a describe block.
If you want to turn on FakeFS one time only for all your examples, you will need to
include FakeFS::SpecHelpers::All.
Alternatively, you can include FakeFS::SpecHelpers in all your example groups using RSpec's
configuration block in your spec helper:
require 'fakefs/spec_helpers'
Spec::Runner.configure do |config|
config.include FakeFS::SpecHelpers
end
If you do the above then use_fakefs will be available in all of your example groups.
You will also need to use before(:each) instead of before(:all) - like many unit test helpers, FakeFS adheres to unit-test isolation principles, in which side-effects of one test should not affect another's. That is why after every test, the gem 'resets' the state of its container, and clears all files from it.

Load a Ruby TestCase Without Running It

I'm trying to write a custom tool that runs ruby unit tests with my customizations.
What I need it to do is to load a certain TestCase from given file(through require or whatever), and then run it after doing some calculations and initializations.
Problem is, the moment I require "test/unit" and a test case, it runs immediately.
What can I do with this?
Thanks.
Since you're running 1.9 and test/unit in 1.9 is merely a wrapper for MiniTest, the following approach should work:
implement your own custom Runner
set MiniTest's runner to your custom runner
Something like (shameless plug from EndOfLine Custom Test Runner, adjusted to Ruby 1.9):
fastfailrunner.rb:
require 'test/unit'
class FastFailRunner19 < MiniTest::Unit
def _run args = []
puts "fast fail runner"
end
end
~
example_test.rb:
require 'test/unit'
class ExampleTest < Test::Unit::TestCase
def test_assert_equal
assert_equal 1, 1
end
def test_lies
assert false
end
def test_exceptions
raise Exception, 'Beware the Jubjub bird, and shun the frumious Bandersnatch!'
end
def test_truth
assert true
end
end
run.rb:
require_relative 'fast_fail_runner'
require_relative 'example_test'
MiniTest::Unit.runner= FastFailRunner19.new
If you run this with
ruby run.rb
the custom FastFailRunner19 will be used, which does nothing.
What about reading file content as a regular text file and doing eval on its content after you initialize/calculate things you say? It may not be sufficient for your needs and may require manual setup and execution of testing framework.
Like that (I put heredoc instead of reading file). Basically content is just a string containing your test case code.
content = <<TEST_CASE
class YourTestCase
def hello
puts 'Hello from eval'
end
end
YourTestCase.new.hello
TEST_CASE
eval content
Note: Altough I'd rather not use eval if there is another way. One should be extra careful when evaling code from string manually in any language.
You could collect the test cases you want to deferred its executions and store them in an array. Afterwards you would create a block execution code. For instance:
test_files = ['test/unit/first_test.rb'] #=> Testcases you want to run
test_block = Proc.new {spec_files.each {|f|load f} } #=> block storing the actual execution of those tests.
Once you're ready to call those testcases you just do test_block.call.
To generalize a bit, when thinking about deferring or delaying code executions, closures are a very elegant and flexible alternative.

Where to put helper functions for rake tasks and test files in Ruby on Rails?

In my Rails application I have a file sample_data.rb inside /lib/tasks as well as a bunch of test files inside my /spec directory.
All these files often share common functionality such as:
def random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
Where should I put those helper functions? Is there some sort of convention on this?
Thanks for any help!
You could create a static class, with static functions. That would look something like this:
class HelperFunctions
def self.random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
def self.otherFunction
end
end
Then, all you would need to do is:
include your helper class in the file you want to use
execute it like:
HelperFunctions::random_address(anyParametersYouMightHave)
When doing this, make sure you include any dependencies in your HelperFunctions class.
If you're sure it's rake only specific, you also can add in directly in RAILS_ROOT/Rakefile (that's probably not the case for the example you use).
I use this to simplify rake's invoke syntax :
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
def invoke( task_name )
Rake::Task[ task_name ].invoke
end
MyApp::Application.load_tasks
That way, I can use invoke "my_namespace:my_task" in rake tasks instead of Rake::Task[ "my_namespace:my_task" ].invoke.
You share methods in a module, and you place such a module inside the lib folder.
Something like lib/fake_data.rb containing
module FakeData
def random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
module_function
end
and inside your rake task just require the module, and call FakeData.random_address.
But, if it is like a seed you need to do every time you run your tests, you should consider adding this to your general before all.
E.g. my spec_helper looks like this:
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
include SetupSupport
config.before(:all) do
load_db_seed
end
end
and the module SetupSupport is defined in spec/support/setup_support.rb and looks as follows:
module SetupSupport
def load_db_seed
load(File.join(Rails.root, 'db', 'seeds.rb'))
end
end
Not sure if you need to load the seeds, or are already doing this, but this is the ideal spot to also generate needed fake data.
Note that my setup support class is defined in spec/support because the code is only relevant to my specs, I have no rake task also needing the same code.

How can I make Aruba use a different ENV["HOME"]?

From the docs:
Per default Aruba will create a directory tmp/aruba where it performs its file operations.
However, my application uses ENV["HOME"] to create and read a file (~/.foorc), so I need Aruba to use a fake ENV["HOME"].
Do I need to set it in some support-file, or is there a way to tell Aruba to its tmp/aruba for files in ENV["HOME"]?
Here is an excerpt of my code that I am testing (obviously I am testing this with Cucumber/Aruba on a much higher level, but the usage of ENV["HOME"] is what is important here.):
def initialize config_path = ""
if config_path.empty?
#config_path = File.join ENV["HOME"], ".todotxt.cfg"
else
#config_path = config_path
end
if file_exists?
super #config_path
validate
end
end
def file_exists?
File.exists? #config_path
end
#....
ask_to_create unless #config.file_exists?
#...
The Specification:
Scenario: todotxt
Given an empty installation
When I run `todotxt`
Then it should pass with:
"""
Should I create a sample config file? [Y/n]
"""
Looking into the implementation in Aruba itself, I could craft something very similar:
File features/support/aruba.rb, is autoloaded by cucumber and implements the Around hook:
# Temporarily enforce an isolated, fake, homedir.
around do |scenario, block|
#__aruba_original_home = ENV["HOME"]
ENV["HOME"] = File.expand_path(File.join("tmp", "aruba"))
block.call
ENV["HOME"] = #__aruba_original_home
end
From now on, a directory tmp/aruba is used as $HOME.
Note that in aruba, this temporary path is configurable, and that above code does not take that into consideration. It will break when the tmp path is configured elsewhere.
Aruba offers a step for just that:
Given a mocked home directory

Resources