RSpec why is before(:each) never executed? - ruby

I have this simple code
require 'json'
module Html
class JsonHelper
attr_accessor :path
def initialize(path)
#path = path
end
def add(data)
old = JSON.parse(File.read(path))
merged = old.merge(data)
File.write(path, merged.to_json)
end
end
end
and this spec (reduced as much as I could while still working)
require 'html/helpers/json_helper'
describe Html::JsonHelper do
let(:path) { "/test/data.json" }
subject { described_class.new(path) }
describe "#add(data)" do
before(:each) do
allow(File).to receive(:write).with(path, anything) do |path, data|
#saved_string = data
#saved_json = JSON.parse(data)
end
subject.add(new_data)
end
let(:new_data) { { oldestIndex: 100 } }
let(:old_data) { {"test" => 'testing', "old" => 50} }
def stub_old_json
allow(File).to receive(:read).with(path).and_return(#data_before.to_json)
end
context "when given data is not present" do
before(:each) do
puts "HERE"
binding.pry
#data_before = old_data
stub_old_json
end
it "adds data" do
expect(#saved_json).to include("oldestIndex" => 100)
end
it "doesn't change old data" do
expect(#saved_json).to include(old_data)
end
end
end
end
HERE never gets printed and binding.pry doesn't stop execution and tests fail with message No such file or directory # rb_sysopen - /test/data.json
This all means that before(:each) never gets executed.
Why?
How to fix it?

It does not print desired message because it fails at the first before block. Rspec doc about execution order
It fails because you provided an absolute path, so it is checking /test/data.json
Either use relative path to the test ie. ../data.json (just guessing),
or full path.
In case of rails:
Rails.root.join('path_to_folder_with_data_json', 'data.json')

Related

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

Thor::Group do not continue if a condition is not met

I'm converting a generator over from RubiGen and would like to make it so the group of tasks in Thor::Group does not complete if a condition isn't met.
The RubiGen generator looked something like this:
def initialize(runtime_args, runtime_options = {})
super
usage if args.size != 2
#name = args.shift
#site_name=args.shift
check_if_site_exists
extract_options
end
def check_if_site_exists
unless File.directory?(File.join(destination_root,'lib','sites',site_name.underscore))
$stderr.puts "******No such site #{site_name} exists.******"
usage
end
end
So it'd show a usage banner and exit out if the site hadn't been generated yet.
What is the best way to recreate this using thor?
This is my task.
class Page < Thor::Group
include Thor::Actions
source_root File.expand_path('../templates', __FILE__)
argument :name
argument :site_name
argument :subtype, :optional => true
def create_page
check_if_site_exists
page_path = File.join('lib', 'sites', "#{site_name}")
template('page.tt', "#{page_path}/pages/#{name.underscore}_page.rb")
end
def create_spec
base_spec_path = File.join('spec', 'isolation', "#{site_name}")
if subtype.nil?
spec_path = base_spec_path
else
spec_path = File.join("#{base_spec_path}", 'isolation')
end
template('functional_page_spec.tt', "#{spec_path}/#{name.underscore}_page_spec.rb")
end
protected
def check_if_site_exists # :nodoc:
$stderr.puts "#{site_name} does not exist." unless File.directory?(File.join(destination_root,'lib','sites', site_name.underscore))
end
end
after looking through the generators for the spree gem i added a method first that checks for the site and then exits with code 1 if the site is not found after spitting out an error message to the console. The code looks something like this:
def check_if_site_exists
unless File.directory?(path/to/site)
say "site does not exist."
exit 1
end
end

Testing after_create with rspec

I have code in my model.
class Foo < ActiveRecord::Base
after_create :create_node_for_foo
def create_node_for_user
FooBar.create(id: self.id)
end
end
and have code in rspec of Foo model
describe Foo do
let (:foo) {FactoryGirl.create(:foo)}
subject { foo }
it { should respond_to(:email) }
it { should respond_to(:fullname) }
it "have mass assignable attributes" do
foo.should allow_mass_assignment_of :email
foo.should allow_mass_assignment_of :fullname
end
it "create node in graph database" do
foo1 = FactoryGirl.create(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
end
end
but my test is failing with message
Failures:
1) Foo create node in graph database on
Failure/Error: FooBar.should_receive(:create).with(id: foo1.id)
(<FooBar (class)>).create({:id=>18})
expected: 1 time
received: 0 times
What might be wrong?
Okay got around with problem
changed this
it "create node in graph database" do
foo1 = FactoryGirl.create(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
end
to
it "create node in graph database" do
foo1 = FactoryGirl.build(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
foo1.save
end
A bit late to the party but actually the above won't work. You can create a custom matcher to do it:
class EventualValueMatcher
def initialize(&block)
#block = block
end
def ==(value)
#block.call == value
end
end
def eventual_value(&block)
EventualValueMatcher.new(&block)
end
Then in your spec do:
it "create node in graph database" do
foo1 = FactoryGirl.build(:foo)
FooBar.should_receive(:create).with(eventual_value { { id: foo1.id } })
foo1.save
end
This means that the mock will not evaluate the block until after the fact and it has actually been set.
In case it helps someone, an updated version of Dan Draper solution to use a custom matcher with a block, would be like this:
# spec/support/eventual_value_matcher.rb
RSpec::Matchers.define :eventual_value do |expected|
match do |actual|
actual == expected.call
end
end
And usage:
require "support/eventual_value_matcher" # or you can do a global require on the rails_helper.rb file
it "xxx" do
foo1 = FactoryGirl.build(:foo)
expect(FooBar).to receive(:create).with(eventual_value(proc { foo1.id }))
foo.save!
end

Rake caches rspec tests or what?

When I run rspec tests via rake task I get
1) Whois::Record::Parser::WhoisUa status_available.expected#created_on
Failure/Error: lambda { #parser.created_on }.should raise_error(Whois::PropertyNotSupported)
But my status_available_spec.rb looks like this:
# encoding: utf-8
# This file is autogenerated. Do not edit it manually.
# If you want change the content of this file, edit
#
# /spec/fixtures/responses/whois.ua/status_available.expected
#
# and regenerate the tests with the following rake task
#
# $ rake spec:generate
#
require 'spec_helper'
require 'whois/record/parser/whois.ua.rb'
describe Whois::Record::Parser::WhoisUa, "status_available.expected" do
before(:each) do
file = fixture("responses", "whois.ua/status_available.txt")
part = Whois::Record::Part.new(:body => File.read(file))
#parser = klass.new(part)
end
describe "#status" do
it do
#parser.status.should == :available
end
end
describe "#available?" do
it do
#parser.available?.should == true
end
end
describe "#registered?" do
it do
#parser.registered?.should == false
end
end
describe "#created_on" do
it do
#parser.created_on.should == nil
end
end
describe "#updated_on" do
it do
#parser.updated_on.should == nil
end
end
describe "#expires_on" do
it do
#parser.expires_on.should == nil
end
end
describe "#nameservers" do
it do
#parser.nameservers.should be_a(Array)
#parser.nameservers.should == []
end
end
end
and as you can see there is no .should raise_error(Whois::PropertyNotSupported) instead it was changed to .should == nil.
Is rake caching that stuff or what?

Ruby EventMachine & functions

I'm reading a Redis set within an EventMachine reactor loop using a suitable Redis EM gem ('em-hiredis' in my case) and have to check if some Redis sets contain members in a cascade. My aim is to get the name of the set which is not empty:
require 'eventmachine'
require 'em-hiredis'
def fetch_queue
#redis.scard('todo').callback do |scard_todo|
if scard_todo.zero?
#redis.scard('failed_1').callback do |scard_failed_1|
if scard_failed_1.zero?
#redis.scard('failed_2').callback do |scard_failed_2|
if scard_failed_2.zero?
#redis.scard('failed_3').callback do |scard_failed_3|
if scard_failed_3.zero?
EM.stop
else
queue = 'failed_3'
end
end
else
queue = 'failed_2'
end
end
else
queue = 'failed_1'
end
end
else
queue = 'todo'
end
end
end
EM.run do
#redis = EM::Hiredis.connect "redis://#{HOST}:#{PORT}"
# How to get the value of fetch_queue?
foo = fetch_queue
puts foo
end
My question is: how can I tell EM to return the value of 'queue' in 'fetch_queue' to use it in the reactor loop? a simple "return queue = 'todo'", "return queue = 'failed_1'" etc. in fetch_queue results in "unexpected return (LocalJumpError)" error message.
Please for the love of debugging use some more methods, you wouldn't factor other code like this, would you?
Anyway, this is essentially what you probably want to do, so you can both factor and test your code:
require 'eventmachine'
require 'em-hiredis'
# This is a simple class that represents an extremely simple, linear state
# machine. It just walks the "from" parameter one by one, until it finds a
# non-empty set by that name. When a non-empty set is found, the given callback
# is called with the name of the set.
class Finder
def initialize(redis, from, &callback)
#redis = redis
#from = from.dup
#callback = callback
end
def do_next
# If the from list is empty, we terminate, as we have no more steps
unless #current = #from.shift
EM.stop # or callback.call :error, whatever
end
#redis.scard(#current).callback do |scard|
if scard.zero?
do_next
else
#callback.call #current
end
end
end
alias go do_next
end
EM.run do
#redis = EM::Hiredis.connect "redis://#{HOST}:#{PORT}"
finder = Finder.new(redis, %w[todo failed_1 failed_2 failed_3]) do |name|
puts "Found non-empty set: #{name}"
end
finder.go
end

Resources