How can I skip some setup for specific Rspec tags? - ruby

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

Related

Show the complete description of an example

In RSpec, how can we generate a string from an example, representing the current meta description of the current example?
Example:
RSpec.describe Api::V1::Public::Signin::ByEmailController, type: :controller do
describe 'POST #create' do
context 'when the provider is "email"' do
context 'when there is a saved email' do
context 'when the password is good' do
it 'signs in' do
expect(current_metadata_description).to eq 'Api::V1::Public::Signin::ByEmailController POST #create when the provider is "email" when there is a saved email when the password is good signs in'
end
end
end
end
end
end
As we can see, the current meta description of the given example is:
Api::V1::Public::Signin::ByEmailController POST #create when the provider is "email" when there is a saved email when the password is good signs in
Thanks.
Edit:
The current_metadata_description is not provided by RSpec. I just invented it to give an example of what I would like to have.
While I am unsure what your overall intentions are for doing this.
Rspec::Core::ExampleGroup.it yields a RSpec::Core::Example to the block. As with all yielding methods one can capture the yielded Object in a block local variable. RSpec::Core::Example has a full_description method which appears to be what you are looking for. RSpec Source.
For Example (no pun intended):
describe String do
context 'Down' do
context 'Deeper' do
it 'can describe itself' do |ex|
# ex is the specific RSpec::Core::Example for this it block
expect(ex.full_description).to eq('String Down Deeper can describe itself')
end
end
end
end
running this will return
.
Finished in 0.003 seconds (files took 0.21998 seconds to load)
1 example, 0 failures
It is imperative that you capture the yielded value if you want the desired output as the block itself is executed in the context of an instance of RSpec::Core::ExampleGroup so calling self.class.metadata[:full_description] will contain the description of the RSpec::Core::ExampleGroup but not the RSpec::Core::Example itself (e.g. "String Down Deeper")
As described in the docs if this is for logging purposes (or something similar where you would like this information for each example) you can access the RSpec::Core::Example in Hooks excerpt:
# Useful for configuring logging and/or taking some action based
# on the state of an example's metadata.
#
# RSpec.configure do |config|
# config.before do |example|
# log example.description
# end
#
# config.after do |example|
# log example.description
# end
#
# config.around do |example|
# log example.description
# example.run
# end
# end
If you really wanted to patch this in (and I am not endorsing it) You could do the following
class RSpec::Core::ExampleGroup
def instance_exec(*args,&blk)
if args.first.is_a?(RSpec::Core::Example)
self.define_singleton_method(:current_metadata_description) do
args.first.full_description
end
end
super(*args,&blk)
end
end
Then your test would be exactly as you stated.
Try to run rspec with --format option:
bundle exec rspec --format documentation
Update:
I don't know any rspec method that would do that for you. One way I can think of is, write the spec description to a file while running the tests and then read the description from it in your custom current_metadata_description method. Here is a tutorial on Generating documentation from specs. Hope that helps!

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)

rspec setup method in separate file

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.

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.

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

Resources