How to check block is called using rspec - ruby

I want to check whether the block is called in my function using rspec. Below is my code:
class SP
def speak(options={},&block)
puts "speak called"
block.call()
rescue ZeroDivisionError => e
end
end
describe SP do
it "testing speak functionality can receive a block" do
sp = SP.new
def test_func
a = 1
end
sp_mock = double(sp)
expect(sp_mock).to receive(:speak).with(test_func)
sp.speak(test_func)
end
end
Below is my error:
SP testing speak functionality can receive a block
Failure/Error: block.call()
NoMethodError:
undefined method `call' for nil:NilClass
# ./test.rb:9:in `speak'
# ./test.rb:25:in `block (2 levels) in <top (required)>'
Could you please help. I spent lots of time in that.

You have to use one of RSpec's yield matcher:
describe SP do
it "testing speak functionality can receive a block" do
sp = SP.new
expect { |b| sp.speak(&b) }.to yield_control
end
end

I think Stefan provided the best answer. However I wanted to point out that you should be testing the behaviour of the code instead of implementation details.
describe SP do
it "testing speak functionality can receive a block" do
sp = SP.new
called = false
test_func = -> () { called = true }
sp.speak(&test_func)
expect(called).to eql(true)
end
end

Related

undefined method 'execute' for nil:NilClass

I am making a tool in ruby which can interact with databases.
I am using amalgalite as an adapter for sqlite3.
Code:
require 'amalgalite'
# this is class RQuery
class RQuery
def db_open(db_name)
#db = Amalgalite::Database.new "#{db_name}.db"
make_class
end
def exec_this(query)
#db.execute(query)
end
def make_class
tables_list = exec_this("select name from sqlite_master where type='table'")
tables_list.each do |table|
#class_created = Object.const_set(table[0].capitalize, Class.new)
#class_created.class_eval do
define_singleton_method :first do
RQuery.new.exec_this("select * from #{table[0]} order by #{table[0]}.id ASC limit 1")
end
end
end
end
def eval_this(input)
instance_eval(input)
end
def code
print '>>'
input = gets
exit if input =~ /^q$/
puts eval_this(input)
code
end
end
Now when I am running the code everything works fine until I call table_name.first
It gives output
vbhv#fsociety ~/git/R-Query/bin $ ruby main.rb
Enter the code or q for quit
>>db_open('vbhv')
users
persons
people
programmers
>>Users.first
/home/vbhv/git/R-Query/lib/r-query.rb:36:in `instance_eval': undefined method `execute' for nil:NilClass (NoMethodError)
Did you mean? exec
from /home/vbhv/git/R-Query/lib/r-query.rb:29:in `block (3 levels) in make_class'
from (eval):1:in `eval_this'
from /home/vbhv/git/R-Query/lib/r-query.rb:36:in `instance_eval'
from /home/vbhv/git/R-Query/lib/r-query.rb:36:in `eval_this'
from /home/vbhv/git/R-Query/lib/r-query.rb:43:in `code'
from /home/vbhv/git/R-Query/lib/r-query.rb:44:in `code'
from /home/vbhv/git/R-Query/lib/r-query.rb:44:in `code'
from main.rb:4:in `<main>'
Now the 'execute' function it is talking about is inside amalgalite. What am I doing wrong here?? Thanks in Advance!
The problem in this was that the new class formed dynamically doesn't know about the connection variable '#db'. Hence the code solves the problem.
#class_created.instance_variable_set(:#database, #db)
A big thanks to Jagdeep Singh.

Pass a ruby &block using rspec

I want to test the functionality of the method using rspec that receives anonymous block and not raise error. Below is my code:
class SP
def speak(options={},&block)
puts "speak called"
block.call()
rescue StandardError => e
puts e.inspect()
end
end
describe SP do
it "testing speak functionality not to raise error" do
sp = SP.new
sp_mock = double(sp)
expect(sp_mock).to receive(:speak).with(sp.speak{raise StandardError}).not_to raise_error
end
end
It is below throwing error
SP testing speak functionality not to raise error
Failure/Error: expect(sp).to receive(:speak).with(sp.speak{raise StandardError})
(#<SP:0x007fead2081d20>).speak(nil)
expected: 1 time with arguments: (nil)
received: 0 times
# ./test.rb:22:in `block (2 levels) in <top (required)>'
Spent a lot of time browsing articles of ruby blocks and ruby documentation but can't figure out.
It's too complicated for no reason. Did you mean this?
it "testing speak functionality not to raise error" do
sp = SP.new
expect {
sp.speak {raise StandardError}
}.to_not raise_error
end

Rspec validation of method definition - Failure/Error

In Rspec, testing whether an instance is able to call method x.
DockingStation.rb
class DockingStation
def release_bike
end
end
Docking_spec.rb
require_relative '../lib/DockingStation'
describe DockingStation do
before(:each) do
#dockstat = DockingStation.new
end
describe "#DockingStation" do
it "Check release method" do
expect(#dockstat).to respond_to(:release_bike)
end
end
end
Currently getting the following error message:
1) DockingStation#DockingStation Check release method
Failure/Error: expect(#dockstat).to respond_to(:release_bike)
expected #<DockingStation:0x007fa518a6da00> to respond to :release_bike
# ./spec/Docking_spec.rb:10:in `block (3 levels) in <top (required)>'
What I'm expecting is for the object #dockstat instantiated in the Docking_spec.rb to respond to the release_bike method defined in DockingStation.rb, but this is not the case.
require_relative '../DockingStation'

Rspec double returning NoMethodError

Not sure if my syntax is right, it doesn't seem to recognise the stub I've passed to my double.
class Robot
def boogie friend
friend.dances
end
end
Test:
describe Robot do
let(:robo_friend){double(:friend, {dances: true})}
it "should have a friend dance too" do
expect(subject.boogie :robo_friend).to be true
end
end
And the error:
Robot should have a friend dance too
Failure/Error: expect(subject.boogie :robo_friend).to be true
NoMethodError:
undefined method `dances' for :robo_friend:Symbol
# ./lib/robot.rb:3:in `boogie'
# ./spec/robot_spec.rb:8:in `block (2 levels) in <top (required)>'
Can anyone see what I'm doing wrong?
This will work, you need to pass the object and not a symbol.
describe Robot do
let(:robo_friend) { double("Friend", dances: true) }
it "should have a friend dance too" do
expect(subject.boogie(robo_friend).to eq(true)
end
end

Pure Ruby rspec test passes without method being defined

I have an rspec test on a pure Ruby model:
require 'spec_helper'
require 'organization'
describe Organization do
context '#is_root?' do
it "creates a root organization" do
org = Organization.new
expect { org.is_root?.to eq true }
end
end
end
My organization model looks like this:
class Organization
attr_accessor :parent
def initialize(parent = nil)
self.parent = parent
end
end
The output when running the tests:
bundle exec rspec spec/organization_spec.rb:6
Run options: include {:locations=>{"./spec/organization_spec.rb"=>[6]}}
.
Finished in 0.00051 seconds
1 example, 0 failures
When I run the test, it passes, despite the fact that the method is_root? doesn't exist on the model. I usually work in Rails, not pure Ruby, and I've never seen this happen. What is going on?
Thanks!
It should be:
expect(org.is_root?).to eq true
When you pass block to expect it is being wrapped in ExpectationTarget class (strictly speaking BlockExpectationTarget < ExpectationTarget). Since you didn't specify what you expect from this object, the block is never executed, hence no error is raised.
You are passing a block to expect, which is never being called. You can see this by setting an expectation on that block
expect { org.is_root?.to eq true }.to_not raise_error
1) Organization#is_root? creates a root organization
Failure/Error: expect { puts "HI";org.is_root?.to eq true }.to_not raise_error
expected no Exception, got #<NoMethodError: undefined method `is_root?' for #<Organization:0x007ffa798c2ed8 #parent=nil>> with backtrace:
# ./test_spec.rb:15:in `block (4 levels) in <top (required)>'
# ./test_spec.rb:15:in `block (3 levels) in <top (required)>'
# ./test_spec.rb:15:in `block (3 levels) in <top (required)>'
Or by just putting a plain raise or puts inside the block, neither of which will be called:
expect { puts "HI"; raise; org.is_root?.to eq true }
The block form is used for expecting that a piece of code raises an exception or not. The correct syntax for checking values is:
expect(org.is_root?).to eq(true)

Resources