RSpec hits an error in code, before it is able to expect an error to be raised - ruby

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.

Related

Rspec: undefined method `StandardError' for EmeraldComponent:Module

In my gem I have the following module:
module EmeraldComponent
def self.create(full_name)
raise StandardError('Base directory for components is missing.') if base_directory_missing?
raise StandardError('An Emerald Component must have a name.') if full_name.empty?
raise StandardError('An Emerald Component must have a namespace.') if simple_name?(full_name)
write_component(full_name)
true
end
def self.write_component(full_name)
## To be implemented
end
def self.simple_name?(full_name)
vet = full_name.split('.')
vet.length == 1
end
def self.base_directory_missing?
not (File.exist?(EmeraldComponent::BASE_DIRECTORY) && File.directory?(EmeraldComponent::BASE_DIRECTORY))
end
end
And among my Rspec tests for this module I have these:
context 'create' do
it 'raises an error if the base directory for components is missing' do
expect {
EmeraldComponent.create('test.component.Name')
}.to raise_error(StandardError)
end
it 'raises an error if it receives an empty string as component name' do
expect {
EmeraldComponent.create('')
}.to raise_error(StandardError)
end
it 'raises an error if it receives a non-namespaced component name' do
expect {
EmeraldComponent.create('test')
}.to raise_error(StandardError)
end
it 'returns true if it receives a non-empty and namespaced component name' do
expect(EmeraldComponent.create('test.component.Name')).to be true
end
It happens that when I run the test all of them are passing, except for the first. This gives me the following error.
1) EmeraldComponent Methods create returns true if it receives a non-empty and namespaced component name
Failure/Error: raise StandardError('Base directory for components is missing.') if base_directory_missing?
NoMethodError:
undefined method `StandardError' for EmeraldComponent:Module
# ./lib/EmeraldComponent.rb:10:in `create'
# ./spec/EmeraldComponent_spec.rb:48:in `block (4 levels) in <top (required)>'
As you may see, it is saying that StandardError is undefined for EmeraldComponent:Module.
But StandardError does not belong to EmeraldComponent:Module!
And besides, this same StandardError is working fine for the other tests!.
I've been fighting this error for a while and then decided to post here. Any suggestions?
You should be doing StandardError.new in place or StandardError in your create method
def self.create(full_name)
raise StandardError.new('Base directory for components is missing.') if base_directory_missing?
raise StandardError.new('An Emerald Component must have a name.') if full_name.empty?
raise StandardError.new('An Emerald Component must have a namespace.') if simple_name?(full_name)
write_component(full_name)
true
end
#victorCui is correct.
Instead of raising StandardError.new(), the recommended approach is to write:
raise StandardError, "message"
See https://github.com/rubocop-hq/ruby-style-guide#exception-class-messages

Rspec raise_error within context doesn't seem to work

I had this written, and it passed.
it 'raises a GitConfigNotFound error when YAML config file cannot be found' do
allow(YAML).to receive(:load_file)
.with(Rails.root.join('config', 'git_config.yml'))
.and_raise(Errno::ENOENT)
expect { described_class::config }.to raise_error GitConfigNotFound
end
Then I tried to put it within a context to match my other tests and it failed. I formatted as shown below. Does anybody have any insight as to why this is happening?
context 'will raise a GitConfigNotFound exception if git config file is missing' do
before do
allow(YAML).to receive(:load_file)
.with(Rails.root.join('config', 'git_config.yml'))
.and_raise(Errno::ENOENT)
end
it { expect(described_class::config).to raise_error GitConfigNotFound }
end
It is giving me this output, which seems to be what I want but for some reason doesn't catch it.:
1) GitConfigsLoader will raise a GitConfigNotFound exception if git config file is missing
Failure/Error: it { expect(described_class::config).to raise_error }
GitConfigNotFound:
Error: git_config.yml not found.
# ./lib/git_configs_loader.rb:9:in `rescue in config'
# ./lib/git_configs_loader.rb:7:in `config'
# ./spec/lib/git_configs_loader_spec.rb:37:in `block (3 levels) in <top (required)>'
Perhaps this is what #PeterAlfvin meant, but I finally foud the answer per another one of his answers! I was using expect(...) rather than expect{...}. The parens executes immediately and blows up instantly and isn't caught by the .to raise_exception. Using the braces allows for the raise_error to execute the except block and catch the error.
context 'when no git_config.yml file is proivded' do
before do
allow(YAML).to receive(:load_file).and_raise(Errno::ENOENT)
end
it { expect{ described_class::config }.to raise_exception GitConfigNotFound }
end

Ruby script raising unexpected backtrace

I have a method that should raise a custom error with a message. When I catch the error and raise my own custom error, it is still raising and printing the backtrace of the original error. I just want the custom error and message. Code below.
Method:
def load(configs)
begin
opts = {access_token: configs['token'],
api_endpoint: configs['endpoint'],
web_endpoint: configs['site'],
auto_paginate: configs['pagination']}
client = Octokit::Client.new(opts)
repos = client.org_repos(configs['org'])
repos.each do |r|
Project.create(name: r.name)
end
rescue Octokit::Unauthorized
raise GitConfigError, "boom"
end
#rescue Octokit::Unauthorized
end
class GitConfigError < StandardError
end
My test (which is failling):
context 'with incorrect git configs' do
before do
allow(loader).to receive(:load).and_raise Octokit::Unauthorized
end
it { expect{loader.load(configs)}.to raise_error(GitConfigError, "boom" ) }
end
Test Output:
GitProjectLoader#load with incorrect git configs should raise GitConfigError with "boom"
Failure/Error: it { expect{loader.load(configs)}.to raise_error(GitConfigError, "boom" ) }
expected GitConfigError with "boom", got #<Octokit::Unauthorized: Octokit::Unauthorized> with backtrace:
# ./spec/lib/git_project_loader_spec.rb:24:in `block (5 levels) in <top (required)>'
# ./spec/lib/git_project_loader_spec.rb:24:in `block (4 levels) in <top (required)>'
# ./spec/lib/git_project_loader_spec.rb:24:in `block (4 levels) in <top (required)>'
If you intend to test the handling of the Octokit::Unauthorized error, then raise the error anywhere before the rescue kicks in. Preferably, someplace where it would actually be raised.
Something like this, for example:
before do
allow(Octokit::Client).to receive(:new).and_raise(Octokit::Unauthorized)
end
And then:
expect{ loader.load(configs) }.to raise_error(GitConfigError, "boom" )
As a side note, I would discourage enclosing all lines of your method in a begin;rescue;end structure; you should enclose only the lines from which you are expecting errors.
You are not testing your code as you think. You have mocked it out.
The line
allow(loader).to receive(:load).and_raise Octokit::Unauthorized
replaces the load method on loader with a stub which just raises the named error.
Remove your before block, and it should test your code as intended. Note as written it will make a real request via Octokit, unless you mock that out instead.

RSpec test ArgumentError on method with parameters

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

Rspec not working, or raise not raising?

I'm working on learning TDD while writing some small ruby programs. I have the following class:
class MyDirectory
def check(dir_name)
unless File.directory?(dir_name) then
raise RuntimeError, "#{dir_name} is not a directory"
end
end
end
and I'm trying to test it with this rspec test.
describe MyDirectory do
it "should error if doesn't exist" do
one = MyDirectory.new
one.check("donee").should raise_exception(RuntimeError, "donee is not a directory")
end
end
It never works, and I don't understand what is wrong from the rspec output.
Failures:
1) MyDirectory should error if doesn't exist
Failure/Error: one.check("donee").should raise_error(RuntimeError, "donee is not a directory")
RuntimeError:
donee is not a directory
# ./lib/directory.rb:4:in `check'
# ./spec/directory_spec.rb:9:in `block (2 levels) in <top (required)>'
I'm hoping this is something simple that I'm missing, but I'm just not seeing it.
If you are checking for an exception, you have to separate that from your test with lambda or the exception will bubble up.
lambda {one.check("donee")}.should raise_error(RuntimeError, "donee is not a directory")
Edit: Since people still use this answer, here is what to do in Rspec 3:
expect{one.check("donee")}.to raise_error(RuntimeError, "donee is not a directory")
The lambda is no longer necessary because the expect syntax takes an optional block.
should is deprecated now so instead of this
-> { described_class.new('InValidParam') }
.should raise_error(RuntimeError)
# or
lambda { described_class.new('InValidParam') }
.should raise_error(RuntimeError)
use this:
expect { described_class.new('InValidParam') }.to raise_error(RuntimeError)
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher

Resources