rspec setup method in separate file - ruby

I am trying to create a setup file for rspec so that my '_spec.rb' test only have 'it' blocks like so:
setup.rb
require 'rspec'
module Setup
describe "setup" do
before(:each) do
#foo = 'hello'
'do something'
end
after(:each) do
'do something'
end
end
spec/test_spec.rb
require_relative '../setup.rb'
include Setup
it "test_sample" do
puts #foo
'do test'
end
This returns undefined variable. Can someone shine light onto my issue? I have multiple tests that I want to share the same setup for, and I don't want to repeat the setup for each test.

Use rspec's configure method for this:
# in spec_helper.rb (or setup.rb)
RSpec.configure do |config|
config.before(:each) do
...
end
config.after(:each) do
...
end
end
See here for more info on setting the configuration.

Related

How to fetch RSpec test name in before(:each) block without a spec_helper

So here's an example basic test suite:
describe "Main test suite" do
it "should run test #1" do
...
end
it "should run test #2" do
...
end
end
I want to add a before(:each) which does some special logic with the full test name (it'll be inserting the test name as a metadata header into all of the HTTP requests made by each test). I've found that using "#{self.class.description}" only captures the test suite name ("Main test suite" in this case), but I also need to capture the test name itself.
I've seen some other similar questions on StackOverflow such as Getting the full RSpec test name from within a before(:each) block, but the answers all involve adding Spec::Runner.configure or RSpec.configure options to the spec_helper.rb, but we're running these tests through a custom environment which doesn't use a spec_helper.rb, so I need a solution that doesn't depend on that.
I've also seen other examples such as How to get current context name of rspec where they are doing the logging in the test itself instead of in a before(:each) block, so they can just do something like: it "should Bar" do |example| puts "#{self.class.description} #{example.description}" end. But we have hundreds of these tests and I don't want to copy-paste the same logic in every test -- this seems like an ideal use case for a before(:each) block.
describe "Main test suite" do
before(:each) do |x|
puts "#{x.class.description} - #{x.example.description}"
end
it "should run test #1" do
...
end
it "should run test #2" do
...
end
end
I got it working by putting this at the top of my file:
RSpec.configure do |config|
config.before(:each) do |x|
do_stuff("#{x.class.description} - #{x.example.description}")
end
end

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

How to access tag filters in a before(:suite)/before(:all) hook in RSpec?

I want to access the tag filters passed in the command line
Command line
rspec --tag use_ff
RSpec config
RSpec.configure do |config|
config.before :suite, type: :feature do
# how do I check if use_ff filter was specified in the command line?
if filter[:use_ff]
use_selenium
else
use_poltergeist
end
end
end
In the before(:suite) hook I want to access the tag filters specified in the command line in the config.
According to the rspec-core code base the inclusion tag filters are stored in inclusion_filter of RSpec.configuration. In theory I should be able to access them as follows:
RSpec.configure do |config|
config.before :suite, type: :feature do
if config.filter[:use_ff] # filter is an alias for inclusion_filter
use_selenium
else
use_poltergeist
end
end
end
But, for some reason, I get an empty hash even when I pass the tag from the command line.
config.filter returns an RSpec::Core::InclusionRules. Looking in its superclass RSpec::Core::FilterRules, we see that it has an accessor .rules which returns a hash of tags, so you can do, for example,
RSpec.configure do |config|
config.before(:suite) do
$running_only_examples_tagged_foo = config.filter.rules[:foo]
end
end
describe "Something" do
it "knows we're running only examples tagged foo", :foo do
expect($running_only_examples_tagged_foo).to be_truthy # passes
end
end
(I'm using RSpec 3.4.)
#Dave Schweisguth's answer is great but note you can't use --only-failures option anymore with it.
(unfortunately, --only-failures doesn't work with a tag filter)

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

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

How can I change ruby log level in unit tests based on context

I'm new to ruby so forgive me if this is simple or I get some terminology wrong.
I've got a bunch of unit tests (actually they're integration tests for another project, but they use ruby test/unit) and they all include from a module that sets up an instance variable for the log object. When I run the individual tests I'd like log.level to be debug, but when I run a suite I'd like log.level to be error. Is it possible to do this with the approach I'm taking, or does the code need to be restructured?
Here's a small example of what I have so far.
The logging module:
#!/usr/bin/env ruby
require 'logger'
module MyLog
def setup
#log = Logger.new(STDOUT)
#log.level = Logger::DEBUG
end
end
A test:
#!/usr/bin/env ruby
require 'test/unit'
require 'mylog'
class Test1 < Test::Unit::TestCase
include MyLog
def test_something
#log.info("About to test something")
# Test goes here
#log.info("Done testing something")
end
end
A test suite made up of all the tests in its directory:
#!/usr/bin/env ruby
Dir.foreach(".") do |path|
if /it-.*\.rb/.match(File.basename(path))
require path
end
end
As you noted you're going to need to set something in the suite.
Something like the following can be used in the setup method. Just call MyLog.setInSuite in your suite and it'll set the level to INFO on setup.
module MyLog
##use_info = false
def MyLog.setInSuite()
##use_info = true
end
def setup
#log = Logger.new(STDOUT)
if (##use_info)
#log.level = Logger::INFO
else
#log.level = Logger::DEBUG
end
end
end
Hmm. I think the way to do it is to change the logging module to test an environment variable, say TEST_SUITE and set the logging level to info if it is set, and debug if it is not.
Then update your test suite to set the TEST_SUITE environment variable at the top, and unset it at the bottom.
Seems a bit clunky, so I'd be interested to see if anyone else has any other ideas.

Resources