I am trying to test on a controller if it raises errors when mass-assignment protected attributes are to be updated.
expect do
post :create, account: {protected_attr: "blahblah"}
end.to raise_error
However Rspec says: expected Exception but nothing was raised
While if in spec file, we remove the expect block, like
post :create, account: {protected_attr: "blahblah"}
There will be an exception when running the spec:
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: protected_attr
How come does rspec's raise_error not capture the exception?
The problem is that you're trying to get an exception on your post :create, account: {protected_attr: "blahblah"} but all this code does is return a http response.
You can't use expect {}.to raise_exception here.
To test the exception you shouldn't do it within the controller spec (I would do it on spec/models/account_spec.rb. Something like:
expect do
described_class.create!(protected_attr: 'blahblah')
end.to raise_exception
On your controller you can just test for the response code to not be success:
post :create, account: {protected_attr: "blahblah"}
expect(response).to_not be_success
expect(response.status).to be(422) # you can be specific about the code you're expecting
This is not exactly answer for your question but it might solve your problem. You don't have test it in controller tests. Just test in in model specs. It is a concern of models in Rails 3.x. In Rails 4 will be used different approach.
Related
I upgraded my Rails Application from 4.2 -> 5.0.0.1.
Other TESTS works fine (e.g. Model, Helper, Feature), but havinf trouble with my Controller Test.
I have read about Keyword arguments in controller & integration tests in Rails 5. So I changed the code structure as given below...
ActionView::Template::Error: nil is not a valid asset source
setup do
#logo = plogos(:main_logo)
end
test "should get edit" do
puts #logo.id // just to check...working fine
get :edit, params: {id: #logo.id}
assert_response :success
end
But I got new error with ActionView.
Is there anyone encountered and fixed the same issue, please help!
Thank you!
You may want to add some logtrace, probably it hints you where it went wrong.
May it be that the main_logo-fixture doesn't have an image? Since Rails 5 image_tag raises this error when given an nil-value, see also: Rails, "nil is not a valid asset source" for a particular image_tag (Carrierwave)
Besides that, typically the new scaffolded code would look as follows:
require 'test_helper'
class LogosControllerTest < ActionDispatch::IntegrationTest
setup do
#logo = plogos(:main_logo)
end
#...
test "should get edit" do
get edit_logo_url(#logo)
assert_response :success
end
#...
end
I have this problem related to testing model errors with Mocha:
This is my controller (Api / Artist controller):
class Api::ArtistsController < ApplicationController
respond_to :json
def create
artist = Artist.new(params[:artist])
if artist.save <-- This is where the test fails
render :json=>artist
else
respond_with artist
end
end
end
This is my model (Artist Model):
class Artist < ActiveRecord::Base
include Core::BaseModel
attr_accessible :name
has_many :albums
validates :name, :presence=>true, :uniqueness=>{:case_sensitive=> false}
default_scope where :deleted=>false
end
This is the test where it fails, about Artist controller:
it "should not save a duplicated artist" do
Artist.any_instance.stubs(:is_valid?).returns(false)
Artist.any_instance.stubs(:errors).returns({:name=>[I18n.t('activerecord.errors.messages.taken')]})
post :create, :format => :json
expect(response).not_to be_success
expect(response.code).to eq("422")
results = JSON.parse(response.body)
expect(results).to include({
"errors"=>{
"name"=>[I18n.t('activerecord.errors.messages.taken')]
}
})
end
When I run the tests, this is the error I get on the above test:
Failure/Error: post :create, :format => :json
NoMethodError:
undefined method `add_on_blank' for {}:Hash
# ./app/controllers/api/artists_controller.rb:17:in `create'
# ./spec/controllers/api/artists_controller_spec.rb:56:in `block (3 levels) in <top (required)>'
I'm starting to use Mocha, so I don't know if there's a way to test the json result for the specific case when I want to test the validation for the duplicated name.
ActiveRecord::Base#errors (i.e. Artist#errors) isn't a simple hash. It's supposed to be an instance of ActiveModel::Errors. You're stubbing it with a hash, and ActiveRecord is trying to call add_on_blank on it, which is failing.
I don't think save invokes is_valid? at all, and I suspect it's running the validations and then trying to call add_on_blank to append an error, but since you've stubbed out errors, that's failing.
This isn't really a good way to test the controller. It's making too many assumptions about the internals of Artist. You're also testing things that aren't part of the controller at all; errors isn't referenced anywhere in the action. The only behavior worth testing in the controller is whether or not it creates an Artist; if that Artist fails to save, that it renders JSON with it; and if the save succeeds, that it redirects. That's all of the controller's responsibility.
If you want to test that errors are rendered a certain way, you should write a separate view spec. If you want to test that missing fields generate errors, you should write a model spec. If you don't want to write a view spec, it's still sufficient to rely on the model to populate errors (tested in a model spec), and in your controller, just test that render is called with json set to the Artist instance.
Generally speaking it's best to avoid stubbing as much as possible, but in this case, the only things I'd consider stubbing are Artist.new to return a mock, and save on that mock to return false. Then I'd check to make sure it rendered with the mock.
The easier option is to just create an actual Artist record, then call post with duplicate params to trigger a validation failure. The downside is that you hit the database, and avoiding that in a controller spec is laudable, but generally more convenient. You could instead do that in a Capybara feature spec if you want to avoid DB hits in your controller specs.
If you want to try testing the way you are, you can manually create an instance of ActiveModel::Errors and populate that, or stub methods on it, and stub out Artist.any_instance.stubs(:errors) to return your mock with ActiveModel::Errors-compatible behavior, but that's a lot of mocking.
One final tip: don't use post :create, :format => :json. Use xhr :post, :create to generate a real Ajax request rather than relying on a format param. It's a more robust test of your routing and response code.
I'm trying to set up specs to properly run with my nested resource.
This is the test code I'm trying to properly set up
it "redirects to the created unit" do
post :create, {:course_id => #course.id , :unit => valid_attributes}
response.should redirect_to(course_unit_path(#course, Unit.last))
end
That essentially should try to create a nested resource "unit" for "course".
Unfortunatly I'm getting the following error on all POST DELETE and PUT tests
Failure/Error: post :create, {:course_id => #course.id , :unit => valid_attributes}
NoMethodError:
undefined method `unit_url' for #<UnitsController:0x000000059f1000>
That makes sense since unit_url should be course_unit_url but it's RSpec calling it...
How can I make RSpec select the right named path?
For all GET tests I passed the :course_id by hand.
This is what I did:
it "redirects to the created unit" do
unit_id = "barry"
Unit.any_instance.should_receive(:save).and_return(true)
Unit.any_instance.stub(:id).and_return(unit_id)
post :create, {:course_id => #course.to_param , :unit => valid_attributes}
response.should redirect_to(course_unit_path(#course, unit_id))
end
I decided that the point of this test was not that it created a new model and redirected it, but simply that it redirects. I have another spec to ensure it creates a new model. Another benefit to this approach is that it doesn't touch the database so it should run a little faster.
I hope that helps.
Edit:
I also just noticed I have this in my before :each section which may be relevant:
Course.stub!(:find).and_return(#course)
Edit again:
In this case, there was code in the controller which was doing the offending call. As per comment below.
I'm using the latest versions of Rails, Rspec and Factory Girl and I'm getting a strange issue when I try to test my create or update logic. The controller in question is an Admin namespaced PostsController and the model is Post. The factory itself just creates a post with a title and a body.
describe 'create' do
before :all do
#new = Factory.build(:post)
end
it 'should be successful' do
post :create, :post => #new
response.should be_success
end
describe 'failure' do
it 'should not create a new page' do
lambda do
post :create, :post => #new
end.should_not change(Post, :count)
end
it 'should render the new template' do
post :create, :post => #new
response.should render_template('new')
end
end
end
The error I keep receiving is:
ActiveRecord::UnknownAttributeError:
unknown attribute: post
I'm probably doing something extremely stupid but I'm just lost right now.
UPDATE
Just in case anyone should ever stumble across this...
I was doing something extremely stupid. I had an error in my controller where instead of calling Post.new(params[:post]) I was calling Post.new(params)...
It would help to know what line it's failing on. If it's failing in the 'before :all' block, then the problem is probably in your factory code, which is presumably specifying a value for a non-existent 'post' attribute of the model.
If that's what the factory is doing, but the 'post' attribute actually should exist, then perhaps you ran this using rspec from the command line without running rake db:test:prepare first. In that case, your 'posts' table structure might not be up to date.
I'm trying to move to rspec for testing, but I can't get controller testing to work with user authentication. Testing a route that doesn't require a user works.
require 'spec_helper'
describe UddsController do
include Devise::TestHelpers
render_views
before (:each) do
#user = User.new(:email => "test#user.com", :username => "test123")
#user.roles << Role.find_or_create_by_name("admin")
#user.save
sign_in #user
end
it "should get index" do
get :index
response.should be_success
end
I just get
2) UddsController should get index
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/udds_controller_spec.rb:21
I'm not using factory_girl. All the example fixes here and on google seem to. I don't see why what I've done wouldn't work. Error is useless in debugging.
I know this question has been asked to death. I've read every blog and forum I can find. I'm at a dead end.
It might be redirecting. be_success returns false for any non-2xx status codes (like 302). Try checking the value of response.status -- if it's redirecting, it probably means your authentication or authorization scheme is producing an unexpected result.
Try adding puts response.body before your assertion, and see what the page you are requesting says. If you can't figure it out from there, perhaps edit your question to include the output?
If you are using devise's "confirmable" module which makes users click a link in an email to confirm their address, then when you use "sign_in" in the tests you also have to fake email confirming. It looks something like this:
sign_in #user
#user.confim!