Expecting errors in rspec tests - ruby-on-rails

I'm trying to expect an error in an rspec test.
lambda {Participant.create!({:user_id => three.id, :match_id => match.id, :team => 1})}.should raise_error StandardError
For now I'm just using StandardError to make sure it's working.
1) StandardError in 'Participant should never allow more participants than players'.
This game is already full. Cannot add another player.
/home/josiah/Projects/Set-Match/app/models/participant.rb:12:in `do_not_exceed_player_count_in_match'
./spec/models/participant_spec.rb:24:
It clearly throws the error, but my test still fails.
Thoughts?

Since some time, but at least in RSpec 2.5, it is possible to use
expect {raise 'boom'}.to raise_error(RuntimeError, /boom/)

Your syntax looks correct. To debug this, simplify to make sure your spec is coded correctly.
it "should raise an error" do
lambda {raise "boom"}.should raise_error
end
And then add more detail until it breaks.
lambda {raise "boom"}.should raise_error(RuntimeError)
lambda {raise StandardError.new("boom")}.should raise_error(StandardError)

I had to fight with the same symptoms:
def boom
raise "boom"
end
boom.should raise_error
The test above fails because raise_error requires should to be called on a Proc (due to technical reasons, I suppose). Thus, wrapping a method call with a lambda works just fine:
lambda { boom }.should raise_error
Unfortunately, the documentation does not say that explicitly and there is no RSpec Exception that reveals this behavior. There is a two year old ticket for that.

Related

Using result of raise_error (capybara)

I'm using capybara to do some web automation.
There are various points in the code where it says things like
raise_error 'Failed as this is a duplicate' if duplicate?
or
raise_error 'Failed to log in' if logged_in? == false
All of this is abstracted to a module and I'd prefer that module to not rely on anything in the models.
What I'm struggling with is how to access that error text when I'm running it, from outside the model.
i.e.
Class Thing
has_many :notes
def do_something
#done = Module::Task.something(self.attribute)
if #done
self.update_attributes(status:'Done')
else
self.notes.new(text: error.text)
end
end
but I can't work out the syntax to get that error text.
Answer: If I understand you correctly then errors that appeared while completing the task
#done = Module::Task.something(self.attribute)
can be accessed via #done.errors.messages
Example: If I have User model where attribute username has 2 validations: presence and format then error messages display like this:
irb(main):019:0* u = User.new
irb(main):022:0* u.save # wont succeed
irb(main):028:0* u.errors.messages
=> {:uid=>["can't be blank", "is invalid"]}
If you want to test error messages with capybara then you can use the syntax like this:
it 'raises jibberishh' do
expect{User.raise_error_method}.to raise_error("jibberishh")
end

How to test assm guard error with rspec?

I have a state machine with some guards to prevent special state transitions.
In my spec, I'm trying to expect guard violation error like this:
expect(violate_guard).to raise_exception
As a result, I receive correct error in my spec test:
Failure/Error: expect(my_model.change_event).to raise_exception
AASM::InvalidTransition:
Event 'change_event' cannot transition from 'current_state'
I'm wondering how should I change my spec to have a satisfied test?
you could try:
expect { violate_guard }.to raise_error
http://rubydoc.info/gems/rspec-expectations/frames
I don't understand well.
If you want a method to wrap the transition violation, you must define a method in the spec
def violate_guard
job = Job.new
job.run ## <= invalid transition
end
then you use the method in spec
expect { violate_guard }.to raise_error

RSpec for should_receive and should_not_receive both passed for Exception

I had a really weird rspec case scenario. I tried to test if my function handles exception correctly. And the following is my code:
in User.rb:
def welcome_user
begin
send_welcome_mail(self)
rescue Exception => exception
ErrorMessage.add(exception, user_id: self.id)
end
end
end
in user_spec.rb
it "adds to error message if an exception is thrown" do
mock_user = User.new
mock_user.stub(:send_welcome_mail).and_raise(Exception)
ErrorMessage.should_receive(:add)
mock_user.welcome_user
end
The test passed, but when I change ErrorMessage.should_receive(:add) to ErrorMessage.should_not_receive(:add), It also passed, any insights?
Since rspec 2.11 exposed one of my tests to exhibit such "abnormality", I decided to raise the issue on github. You may following the discussion at https://github.com/rspec/rspec-mocks/issues/164
Summary: any_instance.should_not_receive is undefined, avoid
What you can try to use instead is .should_receive in combination with .never:
ErrorMessage.should_receive(:add).never

Rspec, expect scope to change by

I have written following test in Rspec:
expect {
...
}.to change( User.where(:profile.exists => true), :count ).by(1)
but this scope is executed only once and it's always the same Array with the same size. How to make rspec execute this scope before and after running the code in expect?
The OPs Solution, Posted As Answer
This may or may not work for anyone else with a similar problem. No corpus was included in the original question, and it was not independently verified.
expect {
# test goes here
}.to change{ User.where(:profile.exists => true).count }.by(1)

Rails ActiveSupport: How to assert that an error is raised?

I am wanting to test a function on one of my models that throws specific errors. The function looks something like this:
def merge(release_to_delete)
raise "Can't merge a release with itself!" if( self.id == release_to_delete.id )
raise "Can only merge releases by the same artist" if( self.artist != release_to_delete.artist )
#actual merge code here
end
Now I want to do an assert that when I call this function with a parameter that causes each of those exceptions, that the exceptions actually get thrown. I was looking at ActiveSupport documentation, but I wasn't finding anything promising. Any ideas?
So unit testing isn't really in activesupport. Ruby comes with a typical xunit framework in the standard libs (Test::Unit in ruby 1.8.x, MiniTest in ruby 1.9), and the stuff in activesupport just adds some stuff to it.
If you are using Test::Unit/MiniTest
assert_raise(Exception) { whatever.merge }
if you are using rspec (unfortunately poorly documented, but way more popular)
lambda { whatever.merge }.should raise_error
If you want to check the raised Exception:
exception = assert_raises(Exception) { whatever.merge }
assert_equal( "message", exception.message )
To ensure that no exception is raised (or is successfully handled) do inside your test case:
assert_nothing_raised RuntimeError do
whatever.merge
end
To check that error is raised do inside your test case:
assert_raise RuntimeError do
whatever.merge
end
Just a heads up, whatever.merge is the code that raises the error (or doesn't, depending on the assertion type).

Resources