How to get the test pass for this error?
Rspec controller and result
context 'invalid confirmation_token' do
subject do
post signup_step5_path,
params: {
user: {
password: 'hoge',
password_confirmation: 'hoge',
confirmation_token: 'wrong_token'
}
}
end
let(:user) { User.find_by(confirmation_token: 'testtesttest') }
it 'does not update user attributes and never create an end_point record' do
expect { subject }.raise_error(ActiveRecord::RecordNotFound)
expected ActiveRecord::RecordNotFound but nothing was raised
controller-method I rescue ActiveRecord::RecordNotFound and render 404 page in the private method.
class Users::SignupController < ApplicationController
layout 'devise'
rescue_from ActiveRecord::RecordNotFound, with: :render404
def step5
@user = User.find_by(confirmation_token: step5_params[:confirmation_token])
raise ActiveRecord::RecordNotFound unless @user
.....
end
private
def render404(error = nil)
logger.info "Rendering 404 with exception: #{error.message}" if error
render file: Rails.root.join('public/404.ja.html'), status: :not_found
end
end
CodePudding user response:
Assuming it's a request spec, the request will return HTTP 404, and you can set an expectation for that:
is_expected.to be_not_found
Side note:
@user = User.find_by(confirmation_token: step5_params[:confirmation_token])
raise ActiveRecord::RecordNotFound unless @user
can be simplified to just:
@user = User.find_by!(confirmation_token: step5_params[:confirmation_token])
CodePudding user response:
First its probably a good idea to explain that the exception matchers will only actually match uncaught exceptions. Thats because its basically just a rescue statement and rescues the exception as it bubbles up the call stack and its intended to test that a peice of code raises an exception which its up to the consumer to catch - that is an example of testing the behavior.
Testing that code raises and rescues a exception on the other hand is testing how it does its job.
def foo
raise SomeKindOfError
end
def bar
begin
raise SomeKindOfError
rescue SomeKindOfError
puts "RSpec will never catch me!"
end
end
describe "#foo" do
it "raises an exception" do
expect { foo }.to raise_exception(SomeKindOfError)
end
end
describe "#bar" do
it "rescues the exception" do
expect { bar }.to_not raise_exception(SomeKindOfError)
end
end
When you use rescue_from
its basically just syntactic sugar for using an around_action callback to rescue the given exception:
class ApplicationController
around_action :handle_errors
private
def handle_errors
begin
yield
rescue SomeKindOfError
do_something
end
end
end
While RSpec did at one point have bypass_rescue
for controller specs the use of controller specs is greatly discouraged by both the Rails and RSpec teams and you're really just testing the implementation instead of the behavior.
Instead you should test what the actual controller does instead of how it does it.
context 'invalid confirmation_token' do
# explicit use of subject is a code smell
before do
post signup_step5_path,
params: {
user: {
password: 'hoge',
password_confirmation: 'hoge',
confirmation_token: 'wrong_token'
}
}
end
let(:user) { User.find_by(confirmation_token: 'testtesttest') }
it 'does not update the users password' do
expect(user.valid_password?('hoge')).to be_falsy
end
it 'returns a 404 - NOT FOUND' do
expect(response).to have_http_status(:not_found)
end
# using Capybara in a feature spec is a better way to do this.
it 'renders something' do
expect(response.body).to match("Oh Noes!")
end
end