rails rspec matcher for exception with a stub method - ruby-on-rails

I am having trouble for the matcher to catch the exception. The controller method simply doing a REST call and get the fruit with the id and I want to test when REST give me an error respond which in rails is the JSON::ParserError. I want to test this case, so I stub out the REST call and raise the exception.
I know the fact that the stubbing work since I am getting that exact error. I believe that I just need a matcher to catch the error when calling the get
In Controller
def show
#fruit = FruitsService::Client.get_fruit(params[:id])
end
spec/controller/fruits_controller_spec.rb
describe '#show' do
before do
context 'when a wrong id is given' do
FruitsService::Client.any_instance
.stub(:get_fruit).with('wrong_id')
.and_raise(JSON::ParserError)
end
it 'receives 404 error code' do
get :show, {id: 'wrong_id'} <------ I think I might need a matcher for this ?
expect(FruitsService::Client.get_fruit('wrong_id')).to raise_error(JSON::ParserError)
end
end
This giving this
Failure/Error: get :show, {id: 'wrong_id'}
JSON::ParserError:
JSON::ParserError

When you want to test behavior, such as the raising of errors, you need to pass a block to expect instead of a parameter, as in:
it 'receives 404 error code' do
expect { get :show, {id: 'wrong_id'} }.to raise_error(JSON::ParserError)
end

Related

RSpec Failing test

rspec/spec/controllers/javascript_controller_spec.rb
describe '#show' do
context 'when the javascript can be found' do
subject { get :show, params: { id: javascript.id } }
it { is_expected.to have_http_status(:ok) }
it 'returns the correct body' do
expect(subject.body).to eq(javascript.to_json)
end
end
context 'When the javascript can\'t be found' do
subject { get :show, params: { id: 'blahdeblah' } }
it { is_expected.to have_http_status(:not_found)}
it 'returns an error' do
expect(subject.body).to eq("{\"code\":404,\"message\":\"Javascript with id 'blahdeblah' not found\"}")
end
end
end
controllers/javascript_controller
class JavascriptController < ApplicationController
# ...
def show
javascript = Javascript.find(params[:id])
javascript_hash = Rails.cache.fetch(javascript.cache_key) {javascript.as_json }
render json: javascript_hash, status: :ok
end
# ...
end
So my first two test are passing but my last two aren't.
I'm getting the error 1) JavascriptController#show When the javascript
can't be found returns an error
Failure/Error: javascript = Javascript.find(params[:id])
ActiveRecord::RecordNotFound:
Couldn't find Javascript with 'id'=blahdeblah
its throwing two of the same errors actually for the last two. I'm trying to test for if the ID is not in the DB then throw an error for not found. Can anyone help me with this issue?
You're writing a controller spec, which is a relatively low-level spec: it will invoke your controller action, but any exception raised from there (like ActiveRecord::RecordNotFound) will bubble straight up into your spec.
To see a more comprehensive view of what will happen with real requests, use a request spec instead. That will pass its request through the full stack, and the exception will then be turned into a 404 response of some sort, as it will in production.
Sorry to say this but RTFM. .find throws RecordNotFound if the "id" cant be found. Your controller is raising an error before it gets to the render line.
In short, the test isn't broken. Your controller doesn't conform to the tests that are written in the test suite so you need to update your controller code so that the test will pass. This is basic Test Driven Development (TDD). Update your controller code so all the tests pass.

undefined method `response_code' for nil:NilClass for IntegrationTest that fails

I have a not-null constraint on a column. I have an IntegrationTest test case that posts an instance without that column. My goal is to assert that response code returned is 400.
My first try was:
test "should fail if no context" do
post trips_url, params: { trip: { state: #trip.state } }
assert_response 400
end
But this fails with Minitest::UnexpectedError: ActiveRecord::NotNullViolation: SQLite3::ConstraintException: NOT NULL constraint failed: trips.context:....
Which I don't really understand. Isn't post something like an HTTP client, and so it shouldn't be affected by the exceptions thrown in the controller?
But anyway, next I tried:
test "should fail if no context" do
assert_raises(ActiveRecord::NotNullViolation) do
post trips_url, params: { trip: { state: #trip.state } }
end
assert_response 400
end
But this fails with Minitest::UnexpectedError: NoMethodError: undefined method `response_code' for nil:NilClass, as if response_code wasn't set since an exception is thrown.
So, my question: How to achieve my goal? When testing an endpoint, I don't really care about what exceptions were thrown; I care about what response it's returned. So, how to assert on the response in the case of a failure?
I'm using Rails 5.1.4.
Depends on if your controller is rescuing the ActiveRecord::NotNullViolation and rendering the 400 ? I imagine controller and spec to be something like below
Controller code
class User < ApplicationController
def create
.....
rescue ActiveRecord::NotNullViolation => e
render text: e.message, status: :bad_request
end
end
Spec code
it 'POST with null data returns bad request' do
post to/user/path
expect(response.code).to eq '400'
end

How do I check both rendering :new and JSON response?

I try to add RSpec test for invalid entry create controller action, which responds to HTTP POST verb.
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program, :faculty_id => faculty.id, :code=>"воруй&убивай")
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Controller:
if #program.save
redirect_to faculty_program_path(faculty, #program)
else
render :new,:json => #program.errors
end
What I get from RSpec
Failure/Error: expect(response).to render_template(:new)
expecting <"new"> but rendering with <[]>
This means that the record has been saved and you have been redirected. If you want to check behaviour when invalid data is passed, do not rely on existing validations as they might change in the future. Just stub valid? method:
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program)
allow_any_instance_of(Program).to receive(:valid?).and_return false
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Stubbing valid? has this advantage that your test will not change when you change your validations. Validations on their own should be tested within model tests, this way single change in the code won't cause multiple test fails.

How to test exception raising in Rails/RSpec?

There is the following code:
def index
#car_types = car_brand.car_types
end
def car_brand
CarBrand.find(params[:car_brand_id])
rescue ActiveRecord::RecordNotFound
raise Errors::CarBrandNotFound.new
end
I want to test it through RSpec. My code is:
it 'raises CarBrandNotFound exception' do
get :index, car_brand_id: 0
expect(response).to raise_error(Errors::CarBrandNotFound)
end
CarBrand with id equaling 0 doesn't exist, therefore my controller code raises Errors::CarBrandNotFound, but my test code tells me that nothing was raised. How can I fix it? What do I wrong?
Use expect{} instead of expect().
Example:
it do
expect { response }.to raise_error(Errors::CarBrandNotFound)
end
In order to spec error handling, your expectations need to be set on a block; evaluating an object cannot raise an error.
So you want to do something like this:
expect {
get :index, car_brand_id: 0
}.to raise_error(Errors::CarBrandNotFound)
See Expect error for details.
I am a bit surprised that you don't get any exception bubbling up to your spec results, though.
get :index will never raise an exception - it will rather set response to be an 500 error some way as a real server would do.
Instead try:
it 'raises CarBrandNotFound exception' do
controller.params[:car_brand_id] = 0
expect{ controller.car_brand }.to raise_error(Errors::CarBrandNotFound)
end

mocha stubs with exception - rspec test not passing

Hey,
I am trying to use Mocha and Rspec to test a scenario where a method always raises some exception.
Here is the controller code I am trying to test:
def add_parent
begin
parent = Entity.find_by_id(params[:parent_id])
if !parent.nil?
#entity.add_parent!(parent)
flash[:success] = "Entity successfully updated."
else
raise "Parent does not exist."
end
rescue
flash[:error] = "Something bad happened. #{$!}"
end
redirect_to #entity
end
Here is the test code:
it "should flash error if exception is thrown when adding parent" do
Entity.any_instance.stubs(:add_parent!).raises(Exception)
lambda do
post :add_parent, :id => #entity[:id],
:parent_id => #parent_entity[:id]
end.should_not change(#entity.parents, :count)
flash[:error].should =~ /something bad happened/i
end
Here is the method which is being stubbed:
def add_parent!(parent)
Entity.transaction do
lock!('lock in share mode')
self.parents << parent
end
end
I am getting the following rspec error, which is pretty uninformative so I don't know how to resolve it..
Failures:
1) EntitiesController POST 'add_parent' for signed-in users allow access with edit permission should flash error if exception is thrown when adding parent
Failure/Error: post :add_parent, :id => #entity[:id],
Exception
# ./app/controllers/entities_controller.rb:81:in `add_parent'
# ./spec/controllers/entities_controller_spec.rb:1010
Wooah, first of all, it's a very bad habit to rescue from a big chunk of code without give the exception class you are expecting here. rescueing everything is no good. Best remove the whole rescueing, maybe use find over find_by_id so rails can catch that 404 error and you aren't bothered about. On the other hand, is this error supposed to happen a lot? Looks like some db-stuff going on so I wouldn't expect it to fail.
Secondly, I think you must test like the manual says about raise_error
http://relishapp.com/rspec/rspec-expectations/v/2-6/dir/built-in-matchers/raise-error-matcher
rescue with no argument will rescue StandardError and its descendants. If you want to catch ALL exceptions, you should rescue Exception as all exceptions descend from class Exception.
Source: http://www.ruby-forum.com/topic/44495

Resources