How to get the feature name in Cucumber hooks.rb - ruby

I need a variable that contains the name of the feature: since it's a feature, do it; since it is another, do that.
I tried these ways, but no success:
Examples:
1)
After do |scenario|
puts scenario.feature.name
end
2)
After do |scenario|
puts scenario.scenario_outline.feature.name
end
p.s.: Translated by Google

I believe this would just be
After do |scenario|
puts scenario.name
end

Finally I found it!
Before do |s|
feature(s.location) # => #<struct Cucumber::Core::Test::Location::Precise
end
def feature(location)
string = File.read(location.file)
document = ::Gherkin::Parser.new.parse(string)
document[:feature]
end
The solution is in the clink below:
https://github.com/cucumber/cucumber-ruby/issues/1432

Related

NilCheck fix on safe navigation operator (&.)

This simple method on a class just run the status method using the safe navigation operator.
def current_status
account&.status
end
But reek report this warning:
MyClass#current_status performs a nil-check [https://github.com/troessner/reek/blob/master/docs/Nil-Check.md]
How can I properly write methods like this to avoid Nil Check?
I've also verified this post from thoughtbot but it seem like "too much" for just a safe navigation operator.
Ruby 2.3.1
The advice from "Example 4" in the linked post is verbose but pretty good :
class MyClass
def initialize(with_account = nil)
#account = Account.new if with_account
end
def current_status
account.status
end
def account
#account || NilAccount.new
end
end
class Account
def status
"Up!"
end
end
class NilAccount
def status
"Down!"
end
end
puts MyClass.new(:with_account).current_status
#=> "Up!"
puts MyClass.new.current_status
#=> "Down!"
If it's "too much" for you, account&.status might be just fine.
Whatever you do : you'll need to test your code as much as possible!
well, tell-dont-ask looks pretty good, but Example 4 looks like an overkill to resolve this specific case.
#andredurao I think, we can use this workaround to pass checks, for some reason reek is fine with it:
def current_status
return unless account
account.status
end

How can I figure out which step I've just executed in Cucumber's AfterStep hook?

I'm writing a method to be executed on the AfterStep callback for Cucumber.
https://github.com/cucumber/cucumber/wiki/Hooks#step-hooks
How can I figure out which step was executed before this hook was called?
Using gem cucumber 2.1.0 and scenario outlines, the scenario object in "Afterstep" is just a test result status, it does not contain the name of the step. I had to use "Before" (called before the first step) that contains a test list.
require 'logger'
$logger = Logger.new(STDOUT)
Before do |scenario|
#count = 0
#tests = Array.new
scenario.test_steps.each{|r|
if( r.name != "AfterStep hook")
#tests << r
end
}
end
AfterStep do |scenario| # run after each step
$logger.info(#tests[#count].name.green)
#count += 1;
end
The logger is required because 'puts' only display when the scenario outline ends.
The AfterStep hook only receives the scenario as parameter.
What you can do, is count the steps, and then get the current one:
AfterStep do |scenario|
#step ||= 0
p scenario.steps[#step].name
#step += 1
end
This will print, in turn, the names of each parameter
Note:
The api has changed slightly. you now need to use 'to_a'
i.e. the Alex Siri's line above would be changed to:
p scenario.steps.to_a[#step].name
Vince has a good solution, I would recommend a refactor:
Before do |scenario|
#tests = scenario.test_steps.map(&:name).delete_if { |name| name == 'AfterStep hook' }
end
You can use the #tests.count instead of the #count variable
I would have made this as comment but I don't have enough reputation yet.
The API has been changed... Based on afterhook doc you can get the result (Cucumber::Core::Test::Result) and the step (Cucumber::Core::Test::Step) like this:
AfterStep do |result, test_step|
#do something
end
You can get the step name with:
stepName = test_step.text
or
stepName = test_step.to_s
I worked it out as follows:
Before do |scenario|
...
#scenario = scenario
#step_count = 0
...
end
AfterStep do |step|
#step_count += 1
end
That keeps the step number updated. In order to get the step name:
#scenario.test_steps[#step_count].name
Vince's answer is great! and SMAG's refactor is cool ! but when I applied the solution on my cucumber test project, I got an error:
undefined method `name' for #<Cucumber::Core::Test::Step:>
so, maybe the answer can update as below:
Before do |scenario|
#tests = scenario.test_steps.map(&:text).delete_if { |text| text == 'AfterStep hook' }
end

How do I search a YAML file?

I have a YAML file books.yaml:
- !ruby.object:Book
title: Ruby for Newbz
author: LeeRoy Jenkins
category: Educational
I already have a method that adds books to this file, but I need a method that can search the YAML file using a regular expression. If no book matches the title then it should raise an exception NoBookfound. If there are any matches, that list should be returned to the caller.
Here is my existing code:
require 'yaml'
require './book'
class Library
attr_accessor :books
def initialize file_name = false
#books = file_name ? YAML::load(File.read(file_name)) : []
end
def add_book(book)
#books.push(book)
end
def search_library(file_name , book)
if
YAML::load(File.read(file_name)).include?(book) == false
raise 'No Book Found'
end
end
end
This is something I tried for the exception portion, but I feel like I'm way off. I'm really new to Ruby and this seems to be a pretty hard task. Does anyone have any ideas?
What you need is to test the class from the loaded object:
book = YAML::load(File.read(file_name))
raise 'No Book Found' unless book.kind_of? Book
There are also some kind_of? alternatives, which you can read about the differences on this nice question
Based on your post, I guess that book param is a Regexp object.
I think you should remove the file_name param from search method, since the file is already loaded on initialize method.
My implemetation would be:
def search_library term
#books.find_all {|b| b.title =~ term}
end
Since you already have "#books" in an enumerable you can use the select method to find books whose title have the search term as a substring.
def search_library(term)
matches = #books.select { |x| x.title.index(term) }
raise "No books have title like '#{term}'" if matches.empty?
matches
end

Passing an object as subject to rspec

I am running rspec tests on a catalog object from within a Ruby app, using Rspec::Core::Runner::run:
File.open('/tmp/catalog', 'w') do |out|
YAML.dump(catalog, out)
end
...
unless RSpec::Core::Runner::run(spec_dirs, $stderr, out) == 0
raise Puppet::Error, "Unit tests failed:\n#{out.string}"
end
(The full code can be found at https://github.com/camptocamp/puppet-spec/blob/master/lib/puppet/indirector/catalog/rest_spec.rb)
In order to pass the object I want to test, I dump it as YAML to a file (currently /tmp/catalog) and load it as subject in my tests:
describe 'notrun' do
subject { YAML.load_file('/tmp/catalog') }
it { should contain_package('ppet') }
end
Is there a way I could pass the catalog object as subject to my tests without dumping it to a file?
I am not very clear as to what exactly you are trying to achieve but from my understanding I feel that using a before(:each) hook might be of use to you. You can define variables in this block that are available to all the stories in that scope.
Here is an example:
require "rspec/expectations"
class Thing
def widgets
#widgets ||= []
end
end
describe Thing do
before(:each) do
#thing = Thing.new
end
describe "initialized in before(:each)" do
it "has 0 widgets" do
# #thing is available here
#thing.should have(0).widgets
end
it "can get accept new widgets" do
#thing.widgets << Object.new
end
it "does not share state across examples" do
#thing.should have(0).widgets
end
end
end
You can find more details at:
https://www.relishapp.com/rspec/rspec-core/v/2-2/docs/hooks/before-and-after-hooks#define-before(:each)-block

How to cache output? (like ob_start() in php)

How to cache "puts" and "print" results, and save it to variable. Like ob_start() and ob_get_contents() in php.
Some people will probably post clever solutions that utilize part of ruby standard library that I'm not familiar with. I can only offer you this one little dirty monkey patch:
module Kernel
alias_method :old_puts, :puts
def puts txt
#cached_output ||= ''
#cached_output += "#{txt}\n"
old_puts txt
end
def cached_output
#cached_output
end
end
puts 'foo'
puts 'bar'
cached_output # => "foo\nbar\n"
require 'stringio'
save_so, $stdout = $stdout, StringIO.new(' ', 'w')
puts 'how now brown cow'
my_so, $stdout = $stdout, save_so
p [:saved_result, my_so.string]
puts 'and this works once again'

Resources