Run after block after specific test in Rspec - ruby

Is there a way I can run the after/before block after/before a specific test using labels?
I have 3 it blocks
describe "describe" do
it "test1" do
end
it "test2" do
end
after(<<what goes here??>>) do
end
end
How do I run the after block only after test2? Is that possible?

You should use contexts to do this. Something like:
describe "describe" do
context 'logged in' do
before(:each) do
# thing that happens in logged in context
end
after(:each) do
# thing that happens in logged in context
end
it "test1" do
end
end
context 'not logged in' do
# No before/after hooks here. Just beautiful test isolation
it "test2" do
end
end
end
Having if/else conditions in before/after blocks is a code smell. Don't do it that way. It'll only make your tests brittle, error prone, and hard to change.

The best way to do this is just use a context. For your example:
describe "AutomateFr33k's fr33ky tests" do
it "runs test1" do
expect(true).to be_true
end
context "do something afterwards" do
after { puts "running something after test2!" }
it "runs test2" do
expect(5).not_to eq(4)
end
end
end

Yes you can do that, have a look here
You can achieve that using metadata in rspec
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
end
describe "Skip hook demo" do
# If prior to RSpec 2.99.0.beta1
after do
puts "before hook" unless example.metadata[:skip]
end
# If RSpec 2.99.0.beta1 or later
after do |example|
puts "before hook" unless example.metadata[:skip]
end
it "will use before hook" do
end
it "will not use before hook", :skip do
end
end

Related

rspec exits when program exits

When RSpec comes across exit in my code, it also exits and no further tests are run. Here is a distilled example:
class Parser
def initialize(argv)
#options = {}
optparse(argv)
end
def optparse(argv)
OptionParser.new do |opts|
opts.on_tail('-h', '--help', 'Show this message') do
puts opts
exit
end
end.parse!(argv, into: #options)
end
end
RSpec.describe Parser do
context 'when -h is passed' do
it 'exits cleanly' do
expect(described_class.new(['-h'])).to raise(SystemExit)
end
end
context 'when --help is passed' do
it 'exits cleanly' do
expect(described_class.new(['--help'])).to raise(SystemExit)
end
end
end
I've also tried exit_with_code(0) and multiple forms of writing these two tests to get the 2nd one to run. Any suggestions?
If I replace
expect(described_class.new(['--help']))
with
expect { described_class.new(['--help']) }
it works.
And since I'm also doing other tests on the same command (in addition to the SystemExit check), I've had to explicitly rescue it and continue on.
it "other test" do
begin
parser.new(args)
rescue SystemExit
end
expect(...)
end
This pattern is kinda ugly, but works. The suppressed exception will require a rubocop override if you use rubocop, but I got that pattern from rubocop's own rspec tests, so ¯\_(ツ)_/¯

Run cleanup step if any it block failed

When one of my it blocks fails, I want to run a cleanup step. When all of the it blocks succeed I don't want to run the cleanup step.
RSpec.describe 'my describe' do
it 'first it' do
logic_that_might_fail
end
it 'second it' do
logic_that_might_fail
end
after(:all) do
cleanup_logic if ONE_OF_THE_ITS_FAILED
end
end
How do I implement ONE_OF_THE_ITS_FAILED?
Not sure if RSpec provides something out of the box, but this would work:
RSpec.describe 'my describe' do
before(:all) do
#exceptions = []
end
after(:each) do |example|
#exceptions << example.exception
end
after(:all) do |a|
cleanup_logic if #exceptions.any?
end
# ...
end
I digged a little into the RSpec Code and found a way to monkey patch the RSpec Reporter class. Put this into your spec_helper.rb:
class RSpecHook
class << self
attr_accessor :hooked
end
def example_failed(example)
# Code goes here
end
end
module FailureDetection
def register_listener(listener, *notifications)
super
return if ::RSpecHook.hooked
#listeners[:example_failed] << ::RSpecHook.new
::RSpecHook.hooked = true
end
end
RSpec::Core::Reporter.prepend FailureDetection
Of course it gets a little more complex if you wish to execute different callbacks depending on the spec you're running at the moment.
Anyway, this way you do not have to mess up your testing code with exceptions or counters to detect failures.

RSpec before blocks not being called before contexts or describes

Given the following code:
RSpec.configure do |config|
config.before(:all) { puts 'before all' }
config.before(:suite) { puts 'before suite'}
config.before(:context) { puts 'before context'}
config.before(:each) { puts 'before each'}
end
RSpec.describe "SomeClass" do
it 'matches some regex' do
puts 'in first it block'
expect('some string').to match(/.*/)
end
describe 'some group of tests' do
puts 'in some group'
context 'when some thing happens' do
puts 'in context'
it 'does something' do
expect(true).to be_truthy
end
end
end
end
I would expect the following output:
before suite
before all
before context
before each
in some group
in context
in first it block
.before each
But instead I get:
in some group
in context
before suite
before all
before context
before each
in first it block
.before each
Meaning that context or describe gets run before any before configuration I've set up.
I expect it to be the first output because of what I've read here and here.
What do I do when I absolutely need code to run before absolutely anything else in the test files? Including (nested) context or describes? And why doesn't it work the way I expect?
Note: I see the same behavior when I include the before :something statements within the scope of the uppermost describe.
(This question is similar to this question, but not the same. I would like to know why my tests are running this way and what the proper RSpec convention is to run a piece of code before absolutely anything else.)
Version info:
RSpec 3.6
- rspec-core 3.6.0
- rspec-expectations 3.6.0
- rspec-mocks 3.6.0
- rspec-support 3.6.0
UPDATE:
It may be helpful to know some context: I'm writing selenium front end automated tests using the selenium-webdriver gem. Before any and all it blocks run, I need to call a function called navigate() (in order to take me to the web page I'm writing the tests for, this function takes about 30 seconds to run because it takes me through two login pages before it gets to where it needs to go) to be called and complete before anything else happens. In my RSpec file I'm using before blocks in an attempt to make this happen, however rspec keeps running tests before the before blocks, and failing.
If you were to put puts "in some group" and puts "in context" into before(:all) blocks, then the output is closer to what you're expecting.
RSpec.configure do |config|
config.before(:all) { puts 'before all' }
config.before(:suite) { puts 'before suite'}
config.before(:context) { puts 'before context'}
config.before(:each) { puts 'before each'}
end
RSpec.describe "SomeClass" do
it 'matches some regex' do
puts 'in first it block'
expect('some string').to match(/.*/)
end
describe 'some group of tests' do
before(:all) { puts 'in some group' }
context 'when some thing happens' do
before(:all) { puts 'in context' }
it 'does something' do
expect(true).to be_truthy
end
end
end
end
outputs
before suite
before all
before context
before each
in first it block
.in some group
in context
before each
.
or, if you did before(:each) you would get
before suite
before all
before context
before each
in first it block
.before each
in some group
in context
.
The reason for the current output is your puts statements for "in some group" and "in context" are being executed when the file is being parsed, not waiting for RSpec at all. If we gave a different example, without Rspec in the mix, imagine we had a file with just
class SomeClass
puts "in class"
def do_something
puts "doing something"
end
end
if we load that file into an irb session or run it on the command line with ruby, we would see "in class" output in the console even though we haven't done anything with that class.

How to ignore or skip a test method using RSpec?

please guide how to disable one of the below test methods using RSpec. I am using Selenuim WebDriver + RSpec combinations to run tests.
require 'rspec'
require 'selenium-webdriver'
describe 'Automation System' do
before(:each) do
###
end
after(:each) do
#driver.quit
end
it 'Test01' do
#positive test case
end
it 'Test02' do
#negative test case
end
end
You can use pending() or change it to xit or wrap assert in pending block for wait implementation:
describe 'Automation System' do
# some code here
it 'Test01' do
pending("is implemented but waiting")
end
it 'Test02' do
# or without message
pending
end
pending do
"string".reverse.should == "gnirts"
end
xit 'Test03' do
true.should be(true)
end
end
Another way to skip tests:
# feature test
scenario 'having js driver enabled', skip: true do
expect(page).to have_content 'a very slow test'
end
# controller spec
it 'renders a view very slow', skip: true do
expect(response).to be_very_slow
end
source: rspec 3.4 documentation
Here is an alternate solution to ignore (skip) the above test method (say, Test01) from sample script.
describe 'Automation System' do
# some code here
it 'Test01' do
skip "is skipped" do
###CODE###
end
end
it 'Test02' do
###CODE###
end
end
Pending and skip are nice but I've always used this for larger describe/context blocks that I needed to ignore/skip.
describe Foo do
describe '#bar' do
it 'should do something' do
...
end
it 'should do something else' do
...
end
end
end if false
There are a number of alternatives for this. Mainly marking it as pending or skipped and there is a subtle difference between them. From the docs
An example can either be marked as skipped, in which is it not executed, or pending in which it is executed but failure will not cause a failure of the entire suite.
Refer the docs here:
https://relishapp.com/rspec/rspec-core/v/3-4/docs/pending-and-skipped-examples/pending-examples
https://relishapp.com/rspec/rspec-core/v/3-4/docs/pending-and-skipped-examples/skip-examples
There are two ways to skip a specific block of code from being running while testing.
Example : Using xit in place of it.
it "redirects to the index page on success" do
visit "/events"
end
Change the above block of code to below.
xit "redirects to the index page on success" do #Adding x before it will skip this test.
visit "/event"
end
Second way: By calling pending inside the block.
Example:
it "should redirects to the index page on success" do
pending #this will be skipped
visit "/events"
end

In what order do RSpec's before, after and around hooks run?

As I faced some issue I decided to check in what order before and after hooks are executed. This is what I did:
require "spec_helper"
describe "The order:" do
before(:all) {
puts "before_all"
}
after(:all) {
puts "after_all"
}
before(:each) {
puts "before_each"
}
after(:each) {
puts "after_each"
}
describe "DESC A" do
before {
puts "A_before"
}
it "A_it_1" do
expect(1).to eq(1)
end
it "A_it_2" do
expect(1).to eq(1)
end
end
describe "DESC B" do
before {
puts "B_before"
}
it "B_it_1" do
expect(1).to eq(1)
end
it "B_it_2" do
expect(1).to eq(1)
end
end
end
and what I got:
The order:
before_all
DESC A
before_each
A_before
after_each
A_it_1
before_each
A_before
after_each
A_it_2
DESC B
before_each
B_before
after_each
B_it_1
before_each
B_before
after_each
B_it_2
after_all
What is going on here ?? Why is after_each run before A_it_1 ?
UPDATE:
adding around(:each) is even more fun:
around(:each) do |example|
puts "around_in"
example.run
puts "around_out"
end
and results:
The order:
before_all
DESC A
around_in
before_each
A_before
after_each
around_out
A_it_1
around_in
before_each
A_before
after_each
around_out
A_it_2
DESC B
around_in
before_each
B_before
after_each
around_out
B_it_1
around_in
before_each
B_before
after_each
around_out
B_it_2
after_all
Your output, and the official output documented on relishapp.com, are correct. What's happening is that rspec needs to run the after(:each)es after each example, because an exception in an after(:each) would cause the example to fail. Before rspec can display the example in the output, it needs to know whether it is green or red, which means the after(:eaches) need to be run before the example's description appears in the output.
However, if you put a puts statement in your actual example, you will see that the before(:each)es occur before it, then the example code is run (including the puts), then the after(:each)es, just as you would expect, and finally, the description of the example is output to screen.
Like you, I was also confused, until I realized that rspec printing out the example's label doesn't coincide with what it's actually doing -- the label only gets printed out once all the before(:all)s, before(:each)es, and after(:each)es are run for the example.
Note: after(:all)s get run after the example label is printed out, because they do not affect the outcome of the test (a warning is generated that an exception occurred in an after(:all) hook, but this does not make a test go red).
RSpec's documentation for before and after hooks specifies the order in which they run. However, RSpec's documentation for around hooks doesn't specify the order in which they run.
This spec tests the order in which around, before and after :all and :each, and examples, execute. When I run it with rspec(-core) 2.14.8, they execute in the order you'd expect:
describe "order in which rspec around/before/after hooks run" do
before :all do
defined?($previous_hook).should be_false # this hook runs first
$previous_hook = "before :all"
end
around :each do |example|
$previous_hook.should == "before :all"
$previous_hook = "around :each 1"
example.run
$previous_hook.should == "after :each"
$previous_hook = "around :each 2"
end
before :each do
$previous_hook.should == "around :each 1"
$previous_hook = "before :each"
end
it "should not raise an exception or print anything" do
$previous_hook.should == "before :each"
$previous_hook = "example"
end
after :each do
$previous_hook.should == "example"
$previous_hook = "after :each"
end
after :all do
# rspec ignores assertion failures and any other exceptions raised here, so all we can do is puts.
# $previous_hook is a global because if it's an instance variable it is "before :all" at this point.
warn "Previous hook was #{$previous_hook}, NOT around :each 2 as expected" unless $previous_hook == "around :each 2"
end
end
Note some possibly surprising things:
self is different in :all and :each blocks, so I needed to use a global rather than an instance variable.
after :all (but not before :all) eats exceptions.
look at all those places .should works! Not that you'd normally want to use it there.
This has been answered above, but to add an answer simple way.
To see in what order the hooks are running, you have to add "puts" statement inside "it" also
So
describe "The order:" do
before(:all) {
puts "before_all"
}
after(:all) {
puts "after_all"
}
before(:each) {
puts "before_each"
}
after(:each) {
puts "after_each"
}
describe "DESC A" do
before {
puts "A_before"
}
it "A_it_1" do
# expect(1).to eq(1) <<<<---- Change Here
puts "Inside the test"
end
end
end

Resources