I have same problem. I don't know how to test an invalid record in this case. Help me please someone.
Need to look on: subject with wrong sms_campaign_id and in "it"
error.log
ActiveRecord::RecordNotFound: Couldn't find SmsCampaign with 'id'=12314151 [WHERE sms_campaigns.company_id = 66 AND sms_campaigns.company_id = ?]
messages_controller_spec.rb
describe PrivateApi::Company::SmsCampaigns::MessagesController do
let(:company) { create :company, :completed, :with_superuser }
let(:sms_campaign) { create :sms_campaign, company: company }
describe 'GET index' do
let(:user) { create(:user, company: company) }
before(:each) { signin user }
context 'when user logged with invalid sms_campaign_id' do
subject(:index_action_invalid) { get :index, sms_campaign_id: 12314151 }
it 'The wrong sms_campaingn_id' do
index_action_invalid
expect(response).to have_http_status(200)
expect(response.content_type).to eq(Mime::JSON)
end
end
end
end
messages_controller.rb
# frozen_string_literal: true
module PrivateApi
module Company
module SmsCampaigns
# Resource controller to fetch all the additional messages of given SMS campaign
class MessagesController < ::PrivateApi::Company::BaseController
def index
sms_campaign = SmsCampaign.where(company: #company).
accessible_by(current_ability, :read).find(params[:sms_campaign_id])
messages = sms_campaign.messages.order(send_at: :desc)
render json: messages
end
end
end
end
end
Assuming you are not rescuing ActiveRecord::RecordNotFound from somewhere in your controller(s), then this code will raise the error, and return a status code 404.
If you are trying to test that the exception will be raised, you need to use the matcher expect { <block_of_code> }.to raise_error <error_class>
If an exception should not be raised, then check your exception handler, because it is not being invoked.
If you want to do this without raising an ActiveRecord::RecordNotFound, you will need to switch find(id) with where(id: id).first
Related
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
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
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])
Im trying to write some tests to check before validation of a model in rspec
class MyClass < ApplicationRecord
before_validation :generate_anonymous_id
def generate_anonymous_id
retries ||= 1
self.uuid = SecureRandom.uuid
self.aid = Digest::SHA256.hexdigest(uuid)[0...15]
raise ActiveRecord::RecordNotUnique if OtherClass.find_by_sfid(aid) ||
MyClass.find_by_aid(aid)
rescue ActiveRecord::RecordNotUnique => error
Rails.logger.warn("Encountered duplicate uuid/aid")
retries += 1
retry if retries <= 3
Rails.logger.warn("Raising exception after 3 retries")
raise error
end
end
here is my rspec
# frozen_string_literal: true
require "rails_helper"
RSpec.describe MyClass, type: :model do
describe "model validation" do
let(:my_class) do
MyClass.create
end
context "valid" do
it "allows to create a user aid" do
expect(MyClass.new.valid?).to be_truthy
end
end
context "duplicate" do
subject do
MyClass.new
end
it "allows to create a user aid" do
subject.uuid = my_class.aid
expect(subject.valid?).to be_falsey
end
end
end
end
Im trying to test the rescue block and my test always passes. I'm not able to override my subject and i dont know what is the mistake im doing.
Thanks in advance
Your before_validation overwrites the subject.uuid, which is happening when you call subject.valid? i.e. generate_anonymous_id forces it to be valid
I stubbed the code which raises the exception and it worked.
context "invalid record" do
context "capture exception" do
let!(:user) { create(:custom_user) }
before { allow(UserUuid).to receive(:find_by_sfid).and_return(true)}
subject { described_class.new}
it "raises exception" do
expect { subject.valid? }.to raise_error(ActiveRecord::RecordNotUnique)
end
end
end
I am trying to create an RSpec test which detects if a request can crash the controller, usually a 500 error. So I want to be able to distinguish between:
nil.invalid_method # raises NoMethodError
from
params.require(:required_parameter) # raises ActionController::ParameterMissing
in a controller in a generic way. When I do a request,feature or controller test it raises an exception:
describe "Post", type: :request do
it 'does not crash when no params given' do
post '/posts' # this line launches an exception
expect(page).to_not have_http_status(500)
end
end
It seems that before RSpec (or Rails I don't know) had a different behaviour, similar to I'm looking for:
rails 4 api rspec test http status code 410
Rspec shows different status code than browser
How to use HTTP status code symbols in RSpec?
How can I do this? Or how would you do?
Thanks for your time.
You can use a controller spec that doesn't render a 500, but raises the exception instead:
describe "PostController", type: :controller do
describe "POST index" do
it 'does not crash with valid params' do
expect {
post :index, { post: { title: 'foo' } }
}.to_not raise_exception
end
end
describe "POST index" do
it 'crashes without params' do
expect {
post :index
}.to raise_exception(ActionController::ParameterMissing)
end
end
end
Also note the curly brackets { ... } after expect.
You can test that the controller does not raise an uncaught exception by using the raise_error matcher:
RSpec.describe "Things", type: :request do
describe "POST /things" do
it "does not raise an error" do
# we pass a block to expect
expect { post things_path }.to_not raise_error
end
end
end
If the exception is rescued in the controller by using the rescue keyword or Rails rescue_from you would test the response code as usual:
class ThingsController < ApplicationController
rescue_from ActionController::ParameterMissing do
head 500
end
def create
raise ActionController::ParameterMissing.new('foo')
end
end
RSpec.describe "Things", type: :request do
describe "POST /things" do
it "work even if the param is not provided" do
post things_path
expect(response).to successful
end
end
end
In this case it is much more useful to test that the response is what you expected it to be - not that it is not a 500.
This is my Spec file:
require 'rails_helper'
RSpec.describe Programmes::ReportsController, :type => :controller do
let!(:programme) { create(:programme) }
context 'authenticated user' do
describe 'GET index' do
it 'responds with a 200 OK status code' do
get :index, params: { id: programme.id }
expect(response).to have_http_status(:success)
end
end
end
end
This is my Factory;
FactoryGirl.define do
factory :programme do
name { Faker::Lorem.word }
description { Faker::Lorem.sentence(3) }
end
end
This is my Controller;
class Programmes::ReportsController < ApplicationController
def index
end
def create
end
end
I can't seem to get this spec to pass. The route works fine in the browser; eg
http://localhost:3000/programmes/{:id}/reports
The error I have is:
Failures:
1) Programmes::ReportsController authenticated user GET index responds with a 200 OK status code
Failure/Error: let!(:programme) { create(:programme) }
NoMethodError:
undefined method `create' for #<RSpec::ExampleGroups::ProgrammesReportsController::AuthenticatedUser::GETIndex:0x007fac78b1b440>
# /Users/mike/.rvm/gems/ruby-2.2.3/gems/actionpack-5.0.0/lib/action_dispatch/testing/assertions/routing.rb:172:in `method_missing'
I am quite new to Ruby (and Rails). I don't think the Programme object is being created in FactoryGirl - but I don't really know how to find out if that's the case
Did you require 'factory_girl' in spec_helper?
When I am running rspec wit pundit version 1.0 on one of my project spec classes I get multiple errors which I haven't seen before. However, when I'm switching to the previous version of pundit (0.3) everything works correctly.
Up to now what I have noticed is that with newer version of pundit #error in create function is not correctly assigned (instead of error class, I get an error message string from the error class).
class ErrorsController < ApplicationController
before_action :set_execution_environment
def authorize!
authorize(#error || #errors)
end
private :authorize!
def create
#error = Error.new(error_params)
authorize!
end
def error_params
params[:error].permit(:message, :submission_id).merge(execution_environment_id: #execution_environment.id)
end
private :error_params
in spec/factories:
FactoryGirl.define do
factory :error, class: Error do
association :execution_environment, factory: :ruby
message "exercise.rb:4:in `<main>': undefined local variable or method `foo' for main:Object (NameError)"
end
end
in spec/controllers/error_controller.rb:
describe 'POST #create' do
context 'with a valid error' do
let(:request) { proc { post :create, execution_environment_id: FactoryGirl.build(:error).execution_environment.id, error: FactoryGirl.attributes_for(:error), format: :json } }
context 'when a hint can be matched' do
let(:hint) { FactoryGirl.build(:ruby_syntax_error).message }
before(:each) do
expect_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(hint)
request.call
end
expect_assigns(execution_environment: :execution_environment)
it 'does not create the error' do
allow_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(hint)
expect { request.call }.not_to change(Error, :count)
end
it 'returns the hint' do
expect(response.body).to eq({hint: hint}.to_json)
end
expect_json
expect_status(200)
end
context 'when no hint can be matched' do
before(:each) do
expect_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(nil)
request.call
end
expect_assigns(execution_environment: :execution_environment)
it 'creates the error' do
allow_any_instance_of(Whistleblower).to receive(:generate_hint)
expect { request.call }.to change(Error, :count).by(1)
end
expect_json
expect_status(201)
end
end
I get the error message
Pundit::NotDefinedError:
unable to find policy Pundit::ErrorPolicy for #<Pundit::Error: {"message"=>"exercise.rb:4:in': undefined
local variable or method foo' for main:Object (NameError)",
"execution_environment_id"=>1}>
since error class is not correctly created. After that every test in error class fail.
My policies:
class AdminOrAuthorPolicy < ApplicationPolicy
[:create?, :index?, :new?].each do |action|
define_method(action) { #user.internal_user? }
end
[:destroy?, :edit?, :show?, :update?].each do |action|
define_method(action) { admin? || author? }
end
end
class ErrorPolicy < AdminOrAuthorPolicy
def author?
#user == #record.execution_environment.author
end
end
I have no such an problem with any other class.
I've been dealing with the same problem for the last half hour, albeit using minitest, and the solution was to run spring stop and then rerun my tests. Hope this helps.