For some reason my Rspec test if failing when I'm expecting an exception to be raised. I'm running Rspec v2.14.1, this is a custom Ruby app, not a Rails app.
Sample code:
# test.rb
class Test
class BadError < Exception ; end
end
Spec File:
# test_spec.rb
require 'spec_helper'
require 'test'
describe Test do
it 'raises an exception' do
expect( raise Test::BadError ).to raise_exception( Test::BadError )
end
end
Result:
F
Failures:
1) Test raises an exception
Failure/Error: expect( raise Test::BadError ).to raise_exception( Test::BadError )
Test::BadError:
Test::BadError
# ./spec/test_spec.rb:6:in `block (2 levels) in <top (required)>'
Finished in 0.0005 seconds
1 example, 1 failure
Not sure how to troubleshoot this either.
You need to put your code in a block so that RSpec can evaluate it, as in:
expect { raise Test::BadError }.to raise_exception( Test::BadError )
When you pass it as a parameter, the error gets raised before RSpec do anything.
Related
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
I have created a custom error class called MyError and a separate class called MyClass that raises this error in its class method self.raiser. I have also created to rspec expectations to test that MyError is raised and that MyError prints the message my error:
class MyError < StandardError
def self.message
'my error'
end
end
class MyClass
def self.raiser
raise MyError
end
end
describe MyClass do
specify{ expect{ MyClass.raiser }.to raise_exception 'my error' }
specify{ expect{ MyClass.raiser }.to raise_exception MyError }
end
Here's what happens when I run rspec:
$ rspec spec/raise_error.rb --format documentation
MyClass
should raise Exception with "my error" (FAILED - 1)
should raise MyError
Failures:
1) MyClass should raise Exception with "my error"
Failure/Error: specify{ expect{ MyClass.raiser }.to raise_exception 'my error' }
expected Exception with "my error", got #<MyError: MyError> with backtrace:
# ./spec/raise_error.rb:9:in `raiser'
# ./spec/raise_error.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/raise_error.rb:14:in `block (2 levels) in <top (required)>'
# ./spec/raise_error.rb:14:in `block (2 levels) in <top (required)>'
Finished in 0.01662 seconds (files took 0.08321 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/raise_error.rb:14 # MyClass should raise Exception with "my error"
Both of these expectations should pass. got #<MyError: MyError> is bizarre and makes no sense at all. It should be got #<MyError: "my error">
Also, I do not think this is an rspec error:
2.2.1 :007 > require 'rspec'
=> true
2.2.1 :008 > require_relative 'spec/raise_error.rb'
=> true
2.2.1 :009 > MyClass.raiser
MyError: MyError
You have to defined the method message as an instance method, not the class method as you tried.
I rewrote the custom error class :
class MyError < StandardError
def message
'my error'
end
end
class MyClass
def self.raiser
raise MyError
end
end
Here is my spec file :
require_relative "../a.rb"
describe MyClass do
it 'expects the Error class message' do
expect { MyClass.raiser }.to raise_exception 'my error'
end
it 'expects the Error class name' do
expect { MyClass.raiser }.to raise_exception MyError
end
end
Lets run the spec:
[arup#Ruby]$ rspec -fd spec/a_spec.rb
MyClass
expects the Error class message
expects the Error class name
Finished in 0.00232 seconds (files took 0.16898 seconds to load)
2 examples, 0 failures
This is something that I've seen before when using RSpec Rails and I believe that I know what is happening, I just don't know how I can get around it.
To me, it appears that the following test should pass. It expects an error, and an error is raised although I assume that the source of the error is what it is tripping up on.
csv_file_spec.rb
require 'spec_helper'
RSpec.describe Cleaner::CSVFile do
context 'when CSV file does not exist' do
let(:file) { Cleaner::CSVFile.new('tmp/file-does-not-exist.csv') }
it 'raises error' do
expect(file).to raise_error
end
end
end
csv_file.rb
module Cleaner
# A CSVFile is a CSV file loaded into memory. It exposes the clean method.
class CSVFile
attr_accessor :raw
def initialize(file)
#raw = File.open(file)
end
end
end
Output
1) Cleaner::CSVFile is not valid
Failure/Error: expect(Cleaner::CSVFile.new('tmp/file-does-not-exist.csv')).to raise_error
Errno::ENOENT:
No such file or directory # rb_sysopen - tmp/file-does-not-exist.csv
# ./lib/cleaner/csv_file.rb:8:in `initialize'
# ./lib/cleaner/csv_file.rb:8:in `open'
# ./lib/cleaner/csv_file.rb:8:in `initialize'
# ./spec/csv_file_spec.rb:7:in `new'
# ./spec/csv_file_spec.rb:7:in `block (2 levels) in <top (required)>'
I can see that the CSVFile object is not able to be initialized because the file does not exist and that'll be why RSpesc can't continue the test but what can I do to get around this?
I get the feeling that there is something fundamentally wrong with my approach to testing that I'm not seeing. I'd rather delegate the error to the standard File class, and not raise my own error messages as the error is verbose enough and I'd only be duplicating effort - should I be implementing my own instead?
Thanks!
For exceptions you should use block or lambda in expect syntax:
it 'raises error' do
expect{ Cleaner::CSVFile.new('tmp/file-not-exist.csv') }.to raise_error
end
You could use stubbing also :
require 'spec_helper'
RSpec.describe Cleaner::CSVFile do
context 'when CSV file does not exist' do
it 'raises error' do
allow(described_class).to receive(:new).and_raise("File not exist")
expect { described_class.new }.to raise_error("File not exist")
end
end
end
Read match message with a string.
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)
I'm having problems getting this simple test to pass on RSpec 2.8.
I want to write a simple test for the absence of parameters on a method that requires them (i.e. ArgumentError: wrong number of arguments ('x' for 'y')).
My test is testing a Gem module method like so:
describe "#ip_lookup" do
it "should raise an ArgumentError error if no parameters passed" do
expect(#geolocater.ip_lookup).to raise_error(ArgumentError)
end
end
My gem module code looks like this:
module Geolocater
def ip_lookup(ip_address)
return ip_address
end
end
My spec runs with this output.
Failure/Error: expect(#geolocater.ip_lookup).to raise_error(ArgumentError)
ArgumentError:
wrong number of arguments (0 for 1)
# ./lib/geolocater.rb:4:in `ip_lookup'
# ./spec/geolocater_spec.rb:28:in `block (3 levels) in <top (required)>'
What am I missing here?
You need to pass a block to #expect, not a regular argument:
describe "#ip_lookup" do
it "should raise an ArgumentError error if no parameters passed" do
expect { #geolocater.ip_lookup }.to raise_error(ArgumentError)
end
end