How to test assm guard error with rspec? - ruby-on-rails

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

Related

RSpec: Force error within negated custom matcher

I have a custom matcher, that has an expect within:
RSpec::Matchers.define :have_access_to do |action|
match do |user|
allow(controller).to receive(:authorize!)
# perform GET request...
expect(controller).to have_received(:authorize!)
response.code == "200"
end
end
This works, as long as I call it this way
it { expect(shop_manager).to have_access_to(:index) }
But when I try to use the negated form not_to, and the expect within the customer matcher fails, the test passes:
it { expect(shop_manager).not_to have_access_to(:index) }
I understand RSpec logic here: When you want the test to fail with not_to and it fails, everything is fine.
But this expect serves as a side condition: I want to check if the whole request has passed the authorize! step.
I know that a matcher should only test one thing, but I use it a lot and it would lead to a lot of code duplication when I add the have_received(:authorize!) check to every single test.
Is there a way to force RSpec to fail, no matter if the call is negated or not?
You could do
RSpec::Matchers.define :have_access_to do |action|
match do |user|
allow(controller).to receive(:authorize!)
# perform GET request...
fail "failed to receive authorize" unless controller.received_message?(:authorize!)
response.code == "200"
end
end
Using old rspec shoulda syntax. However, I get a deprecation warning for this
Using received_message? from rspec-mocks' old :should syntax without explicitly enabling the syntax is deprecated.

RSpec is ignoring my rescue block in controller

I have a Rails controller which does a health check of the database, like this:
def health_check
begin
status = ActiveRecord::Base.connected? ? 'UP' : 'DOWN'
rescue
status = 'DOWN'
end
render text: status
end
I'm trying to create a RSpec controller spec for this, and the specs for positive and negative responses work, but when I try to test the rescue block, RSpec seems to be ignoring it:
RSpec.describe(HealthCheckController) do
context 'When the check raises an exception' do
before :each do
allow(ActiveRecord::Base).to receive(:connected?).and_raise(OCIException) # Using Oracle
end
it 'should render text DOWN' do
# First attempt
get :health_check
expect(response.body).to eq 'DOWN'
# Second attempt
expect { get :health_check }.to raise_error
expect(response.body).to eq 'DOWN'
end
end
end
I tried the spec with both of the code inside the it block above (separatedly).
For the first, RSpec failed with this:
Failure/Error: get :health_check
OCIException:
OCIException
For the second, it also failed, with this more "familiar" message instead:
Failure/Error: expect(response.body).to eq 'DOWN'
expected: "DOWN"
got: ""
(compared using ==)
I also checked the HTTP code being returned by response, and it's 200, so the response itself is fine, no 500 error.
It's as if RSpec is simply bypassing the rescue block and not running it. What may be causing this? I'm not using the bypass_rescue RSpec method anywhere, this is also a new project.
Using:
Rails 4.2.6
Rake 10.5.0
RSpec-core 3.3.2
RSpec-rails 3.3.3
Actually, the problem has nothing to do with the rescue block in your controller. Rather, it is caused by the fact that you have overriden ActiveRecord::Base#connected? by stubbing it in your before block.
Calling render in the controller initiates a connection to the database. Somewhere in the process ActiveRecord::Base#connected? gets called (twice, actually), but instead of returning what it is supposed to return, it raises an exception that you have defined in your setup.
In your first example, the exception is raised before your expectation, thus the explicit exception name in the failure message.
In your second example, the exception is suppressed by your raise_error expectation, so RSpec is able to proceed to the next one. Where it fails because the render call in the controller never runs (due to the error), and as a result, the response body never gets a chance to be populated.
As a confirmation that ActiveRecord::Base#connected? does get called when you call render, try running the following spec. You will see that it's green, meaning that the said method has been called twice in the process. You can also try replacing render with head :ok and will see that in this case ActiveRecord::Base#connected? gets called only once.
RSpec.describe ApplicationController do
controller do
def index
render json: ''
end
end
specify 'test' do
expect(ActiveRecord::Base).to receive(:connected?).twice
get :index
end
end

Testing PG Database Constraints with RSpec

I'm trying to test PG database constraints in a rails 4 using RSpec, and I'm not sure how to set it up.
My thought was to do something like this:
before do
#subscriber = Marketing::Subscriber.new(email: "subscriber#example.com")
end
describe "when email address is already taken" do
before do
subscriber_with_same_email = #subscriber.dup
subscriber_with_same_email.email = #subscriber.email.upcase
subscriber_with_same_email.save
end
it "should raise db error when validation is skipped" do
expect(#subscriber.save!(validate: false)).to raise_error
end
end
When I run this, it does in generate an error:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint
However, the test still fails.
Is there a proper syntax to get the test to pass?
Try
it "should raise db error when validation is skipped" do
expect { #subscriber.save!(validate: false) }.to raise_error
end
For more information, check the more info on rspec-expectations expect-error matchers
Hope this helps!
Slight modification to #strivedi183's answer:
it "should raise db error when validation is skipped" do
expect { #subscriber.save!(validate: false) }.to raise_error(ActiveRecord::RecordNotUnique)
end
The justification for being more verbose with the error class is that it protects you from having other possible checks that may raise an error that are not related to specific duplication error you wish to raise.

rspec expect redirect_to fail

Sometimes new (very DRY) rspec syntax makes me crazy...
Rspec v 2.14.1
describe "POST create" do
subject { post :create, contractor: valid_params }
context "by user" do
before { sign_in #legal.user }
it "contractor successful created" do
expect { subject }.to redirect_to(contractor_path(assigns(:contractor).id))
I have error & question here:
NoMethodError: # :contractor variable not defined
undefined method `id' for nil:NilClass
It seems that expect take an operator before controller method post executes, because I try to raise this method.
My code:
def create
#contractor = Contractor.restrict!(current_accreditation).new(permitted_params) # TODO move to the IR::Base
if #contractor.save
current_accreditation = #contractor.create_legal!(user: current_user) # TODO legal create
redirect_to(#contractor)
else
render(:new)
end
end
Secondly, why I have an error when try
expect(subject).to ...
Why {} works, but () no? In relish docs this method work great: https://www.relishapp.com/rspec/rspec-rails/docs/matchers/redirect-to-matcher
Kinda unrelated but I've found the following helpful:
Use expect {} when you want to test before/after of whatever's in the block. eg. expect { subject } to change(User, :count) - you want to check the count before, then after, to work out the difference and see if it actually changed.
Use expect () to verify the outcome of a block, eg. that a redirect occurred, or something got assigned, etc.
Your test with the assigns(:contractor) doesn't work because you're using the {} notation - so it's trying to work out the assigns(:contractor).id both before and after evaluating the subject (and of course, before the subject, it doesn't exist).

Expecting errors in rspec tests

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.

Resources