How to raise standard error from a rspec test? - ruby

So I have an rspec test that I am trying to get my program to validate against:
code snippet from rspec test:
context '#validate(test_tool)' do
it { expect(test_tool.validate).to raise_error StandardError }
code snippet from test_tool (test_tool is the program I wrote to pass the rspec test) Inside test_tool there are multiple methods, but I just pulled out the one method that the rspec test is looking for:
def self.validate
raise StandardError
end
I have googled everywhere. No matter how I attempt to raise this error. I keep getting an error. Please see snippet of console output:
example at ./spec/training_site/spec_test.rb:56 (FAILED - 1)
Failures:
1) MyClass #validate(test_tool)
Failure/Error: it { expect(MyClass.validate).to raise_error StandardError }
StandardError:
StandardError
Line 56 is just the 2nd line from the 1st code snippet.
Basically, I'm looking for troubleshooting and or example on how I should accomplish this.

This is correct spec. You should use {} (block brackets) since you are waiting for it to raise error. Simple brackets that you have use are for values checking.
context '#validate(test_tool)' do
it { expect{test_tool.validate}.to raise_error StandardError }

Related

RSpec 3: expect object.do_something to NOT raise a particular kind of error

I would like to test whether a particular method does NOT raise an error of class AError. It can raise BError, ArgumentError, almost any other kind of error, or no error, just not AError. Is there any non-deprecated (as of RSpec 3) way to do this?
I tried
expect { object.do_something }.not_to raise_error(AError)
but I get
ArgumentError:
`expect { }.not_to raise_error(SpecificErrorClass)` is not valid,
use `expect { }.not_to raise_error` (with no args) instead
The problem with the argument-less approach is that the test will fail on ANY kind of error, when it should pass on anything except for an AError.
This documentation doesn't seem to help: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher
It seems that the old versions of RSpec have a way to handle this type of situation, and I don't understand what happened to RSpec in the new version.
So I'm confused. Thanks.
This specific syntax solved the problem:
it 'do_something does not raise AError' do
begin
expect { object.do_something }.not_to raise_error
rescue RSpec::Expectations::ExpectationNotMetError => e
expect(e.message).not_to include 'AError'
end
end
You can read discussion about why this was removed here https://github.com/rspec/rspec-expectations/issues/231
If you really must to test this case, you can do this differently:
begin
object.do_something
rescue StandardError => e
# expect e to not be AError
end

How can I check if my subject raises an exception?

I'm currently creating an object in subject and need to test if this raises an exception. The following code illustrates what I'm trying to achieve:
describe MyClass do
describe '#initialize' do
subject { MyClass.new }
it { is_expected.not_to raise_error(Some::Error) }
end
end
I have a feeling I'm going about this the wrong way. What is the preferred way to set the subject to a new object, without creating the object twice?
Update
My problem was two-fold. Firstly, this syntax does not work:
it { is_expected.not_to raise_error }
Using expect inside an it block does, however (as pointed out by Jimmy Cuadra):
it 'does not raise an error' do
expect { subject }.not_to raise_error
end
I am not well enough acquainted with RSpec to tell you why this is.
Secondly, since RSpec 3.0.0.beta1, it is longer possible to use raise_error with a specific error class. The following, therefore, is invalid:
expect { subject }.to raise_error(Some::Error)
For more information, see
Rspec 3.0.0.beta1 changelog
Consider deprecating `expect { }.not_to raise_error(SpecificErrorClass)` #231
Remove expect {}.not_to raise_error(SomeSpecificClass) #294
If I'm understanding correctly, you're trying to test if instantiating a class causes an exception. You would just do this:
describe MyClass do
it "doesn't raise an exception when instantiated" do
expect { subject }.not_to raise_error
end
end
The right way to do that through is_expected syntax is to wrap your subject value by a Proc, like the following example:
describe MyClass do
describe '#initialize' do
subject { -> { MyClass.new } }
it { is_expected.not_to raise_error(Some::Error) }
end
end
This way is more accurate, because sometimes your use case is to expect that specific kinds of exceptions should not be thrown (while others are allowed to be thrown). This approach will cover such use cases.

rspec: raise_error usage to match error message

I raised an error using raise(ConfigurationError.new(msg))
I tried to test this with rspec:
expect {
Base.configuration.username
}.to raise_error(ConfigurationError, message)
But this doesn't work. How can I test this? The goal is to match message.
You can match error message with regex:
it { expect{ Foo.bar }.to raise_error(NoMethodError, /private/) }
This will check if NoMethodError raised with private method message not undefined method.
Will be useful because NoMethodError.new didn't pass tests even with same error message.
Make sure you are using rspec > 2.14.0 and take a look at this commit:
https://github.com/rspec/rspec-expectations/commit/7f02b503d5ae48d1141b6465acd0a7a4e1bb84dd
it "passes if an error instance is expected" do
s = StandardError.new
expect {raise s}.to raise_error(s)
end
An alternative solution is to use a block where you assert on various properties of |error|. Using the names in the original question:
expect { Base.configuration.username }
.to(raise_error do |error|
expect(error).to be_a(ConfigurationError)
expect(error.message).to eq 'My configuration message'
end)

Why doesn't MiniTest::Spec have a wont_raise assertion?

Ruby's Test::Unit has assert_nothing_raised. Test::Unit has been replaced by MiniTest. Why don't MiniTest's assertions / expectations have anything parallel to this? For example you can expect must_raise but not wont_raise.
MiniTest does implement assert_nothing_raised in its Test::Unit compatibility layer, but in its own tests (MiniTest::Unit and MiniTest::Spec) it does not implement any test like this. The reason is, the programmer argues, that testing for nothing raised is not a test of anything; you never expect anything to be raised in a test, except when you are testing for an exception. If an unexpected (uncaught) exception occurs in the code for a test, you'll get an exception reported in good order by the test and you'll know you have a problem.
Example:
require 'minitest/autorun'
describe "something" do
it "does something" do
Ooops
end
end
Output:
Run options: --seed 41521
# Running tests:
E
Finished tests in 0.000729s, 1371.7421 tests/s, 0.0000 assertions/s.
1) Error:
test_0001_does_something(something):
NameError: uninitialized constant Ooops
untitled:5:in `block (2 levels) in <main>'
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
Which is exactly what you wanted to know. If you were expecting nothing to be raised, you didn't get it and you've been told so.
So, the argument here is: do not use assert_nothing_raised! It's just a meaningless crutch. See, for example:
https://github.com/seattlerb/minitest/issues/70
https://github.com/seattlerb/minitest/issues/159
http://blog.zenspider.com/blog/2012/01/assert_nothing_tested.html
On the other hand, clearly assert_nothing_raised corresponds to some intuition among users, since so many people expect a wont_raise to go with must_raise, etc. In particular one would like to focus an assertion on this, not merely a test. Luckily, MiniTest is extremely minimalist and flexible, so if you want to add your own routine, you can. So you can write a method that tests for no exception and returns a known outcome if there is no exception, and now you can assert for that known outcome.
For example (I'm not saying this is perfect, just showing the idea):
class TestMyRequire < MiniTest::Spec
def testForError # pass me a block and I'll tell you if it raised
yield
"ok"
rescue
$!
end
it "blends" do
testForError do
something_or_other
end.must_equal "ok"
end
end
The point is not that this is a good or bad idea but that it was never the responsibility of MiniTest to do it for you.
If you need it:
# test_helper.rb
module Minitest::Assertions
def assert_nothing_raised(*)
yield
end
end
And to use it:
def test_unknown_setter
assert_nothing_raised do
result.some_silly_column_name = 'value'
end
end
This bothered me enough to dig into the MiniTest sources and provide an implementation in my spec_helper.rb file:
module MiniTest
module Assertions
def refute_raises *exp
msg = "#{exp.pop}.\n" if String === exp.last
begin
yield
rescue MiniTest::Skip => e
return e if exp.include? MiniTest::Skip
raise e
rescue Exception => e
exp = exp.first if exp.size == 1
flunk "unexpected exception raised: #{e}"
end
end
end
module Expectations
infect_an_assertion :refute_raises, :wont_raise
end
end
Hope this proves helpful to someone else who also needs wont_raise. Cheers! :)

Does should_receive do something I don't expect?

Consider the following two trivial models:
class Iq
def score
#Some Irrelevant Code
end
end
class Person
def iq_score
Iq.new(self).score #error here
end
end
And the following Rspec test:
describe "#iq_score" do
let(:person) { Person.new }
it "creates an instance of Iq with the person" do
Iq.should_receive(:new).with(person)
Iq.any_instance.stub(:score).and_return(100.0)
person.iq_score
end
end
When I run this test (or, rather, an analogous one), it appears the stub has not worked:
Failure/Error: person.iq_score
NoMethodError:
undefined method `iq_score' for nil:NilClass
The failure, as you might guess, is on the line marked "error here" above. When the should_receive line is commented out, this error disappears. What's going on?
Since RSpec has extended stubber functionality, now following way is correct:
Iq.should_receive(:new).with(person).and_call_original
It will (1) check expectation (2) return control to original function, not just return nil.
You're stubbing away the initializer:
Iq.should_receive(:new).with(person)
returns nil, so Iq.new is nil. To fix, just do this:
Iq.should_receive(:new).with(person).and_return(mock('iq', :iq_score => 34))
person.iq_score.should == 34 // assert it is really the mock you get

Resources