RoutingError with RSpec Controller test on Scoped Route - ruby-on-rails-3

So I have a route that looks like this:
scope "4" do
scope "public" do
scope ":apikey" do
resources :shops
end
end
end
And a bunch of controller specs, an example of which looks like this:
describe ShopsController do
describe "when responding to a GET" do
context "#new" do
it "should create a new instance of the shop class" do
get :new
#shop.should_not_be_nil
end
end
end
end
In rake routes, as well as via a web browser, this controller/action works fine. However, RSpec throws:
1) ShopsController when responding to a GET#new should create a new instance of the shop class
Failure/Error: get :new
ActionController::RoutingError:
No route matches {:controller=>"shops", :action=>"new"}
# ./spec/controllers/shops_controller_spec.rb:9:in `block (4 levels) in <top (required)>'
When I remove the scope statements from the route, the tests work fine. Is there a way to "inform" RSpec of the route scopes?
Thanks in advance!

According to your routes, :apikey is a required parameter, so you need to add it to your get:
get :new, :apikey => "something"
Also you should change the expectation in your next line to this:
assigns[:shop].should_not be_nil
Use assigns to check the controller's instance variables, and separate should_not from the matcher with a space, not an underscore. That last bit takes some getting used to.

Related

Rails path helper generate dot or undefined method instead of slash

Route defined as follows
resources :purchases do
collection do
put :wirecardtest
end
end
Controller actions redirect in one of the following manners with associated error generated
format.html { redirect_to wirecardtest_purchase_path(#purchase)
undefined method `wirecardtest_purchase_path'
format.html { redirect_to wirecardtest_purchases_path(#purchase)
/purchases/wirecardtest.44
Behaviour is identical when putting code in view.
The ressource is defined in plural mode, as it ought to be. The redirect, as it is supposed to call a specific ressource should call the singular model-action mode (in plural it would generate the period).
I don't understand how I got into this damned-if-you-do, damned-if-you-don't position.
wirecardtest_purchases PUT /purchases/wirecardtest(.:format) purchases#wirecardtest
That's your mistake right there.. the path is generated as 'wirecardtest_purchases' but you are using 'wirecardtest_purchase' note the missing 's' to pluralize the 'purchase'.
Remember its a collection. So the path method is pluralized by rails.
When in doubt rake routes :)
---Update---
Improving the answer (check comments). Need here is to actually define a route as :member and not a :collection if you want to act upon a single object. Referring to Rails Docs,
resources ::purchases do
member do
get 'wirecardtest'
end
end

No route matches controller in rails app

I have an app where I'm creating a get action called "new_911". When I put new_911_call_path in the application layout I get an error "no route matches new_911 controller: calls". Yet there is an action in the calls controller called new_911. What am I doing wrong?
Calls Controller:
def new_911
#call = Call.new :call_status => "open"
respond_with #call
end
application.html.erb
<li><%= link_to 'New 911 Call', new_911_call_path %></li>
routes.rb
resources :calls do
member do
post 'close'
post 'cancel'
post 'note'
get 'new_return'
get 'duplicate_call'
get 'edit_times'
put 'update_billing'
get 'new_911'
end
rake routes:
new_911_call GET /calls/:id/new_911(.:format) calls#new_911
You need to add the parameter to the route. You're using a member route so you need to add the id parameter, take a look of this. You may need to change that route.
Figured it out. I was using a member instead of a collection. Also using new_911 gave me a constant error so I changed it to EmergencyCalls for my controller schema and utilized the normal "new" action. Added resources :emergency_calls to my routes file and it worked.
Sorry for the goof.

How to test Model Errors with Mocha and rspec?

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.

calling specific url in Rspec in integration test

I'm trying to do the following in rspec (know that I shouldn't have hardcoded value) in an integration test:
get '/api/get-other-items?id=5109'
The closest I could find was: call a specific url with rspec but it selects only a single item. I have tried the following:
get :controller => 'api', :action => 'get-other-items', :id => '5109'
get 'api/get-other-items', :id => '5109'
These are giving me a bad argument(expected URI object or URI string)
If I run as
get get_other_items, :id => '5109' q
I get
undefined local variable or method `get_other_items' for #<RSpec::Core::ExampleGroup::Nested_1:0x007f938fd65590>
but the route does exist:
Mon Jan 23$ rake routes | grep get_other_items
get_other_items /api/get-other-items(.:format) {:controller=>"api", :action=>"get_other_items"}
How would I perform this simple get?
thx
update for answer 1 comment
here's the rspec code in question:
it "testing getting other items for menu item" do
get get_other_items_path(:id => '5109')
JSON.parse(response.body)
puts response.body
Mon Jan 23$ rspec requests/get_other_items_spec.rb
F
Failures:
1) GetOtherItems testing getting other items for menu item
Failure/Error: JSON.parse(response.body)
JSON::ParserError:
743: unexpected token at 'this is not found '
# ./requests/get_other_items_spec.rb:19:in `block (2 levels) in <top (required)>'
Finished in 13.57 seconds
1 example, 1 failure
Failed examples:
The call should be get get_other_items_path(:id => 5109) - you need to add path to the name of the route, or url if you want the full URL instead of a relative path.
Your route doesn't look like it's taking a :id as a parameter, if you want to send an :id I would expect to see the following:
get_other_items /api/get-other-items/:id(.:format) {:controller=>"api", :action=>"get_other_items"}
Given the structure of your generated route I assume that you are using match to define the route (There is no HTTP verb in your route). To fix it try:
match 'api/get-other-items/:id' => 'api#get_other_items'
If instead you are using restful routes then it looks like you have specified a collection route rather than a member route. A collection route doesn't take an :id and is designed to return many records of the type you have specified. To make it a member route use the following:
resources :api do
get 'get_other_items', :on => :member
end
Once you get this working you should be able to try the following in rspec in your ApiController spec:
get :get_other_items, :id => '5109'
If neither of these options work please post your routes entry so we can try something else.

RSpec and CanCan Controller Testing

I'm using RSpec and CanCan in a project. I'm testing my permission logic in specs related to the Ability class. For the controllers I really just want to make sure I'm doing an authorization check. I set up a controller macro, but it doesn't seem to be working correctly.
So really I have two questions. One, is the strategy sufficient for testing the permission logic of my controllers (or should I be testing the controller authorization logic more)?
Two, does anyone see what I'm doing wrong to make this not work?
#plan_orders_controller.rb
def approve
plan_order = PlanOrder.find(params[:id])
authorize! :update, plan_order
current_user.approve_plan_order(plan_order)
redirect_to plan_order_workout_plan_url(plan_order)
end
#controller_macros.rb
def it_should_check_permissions(*actions)
actions.each do |action|
it "#{action} action should authorize user to do this action" do
#plan_order = Factory(:plan_order, :id=>1)
ability = Object.new
ability.extend(CanCan::Ability)
controller.stub!(:current_ability).and_return(ability)
get action, :id => 1
ability.should_receive(:can?)
end
end
end
The output I get from RSpec is the following
Failure/Error: ability.should_receive(:can?)
(#<Object:0x00000006d4fa20>).can?(any args)
expected: 1 time
received: 0 times
# ./spec/controllers/controller_macros/controller_macros.rb:27:in `block (2 levels) in it_should_check_permissions'
I'm not really sure which method I should be checking when I call !authorize in the controller (or it is automatically called via load_and_authorize_resource)
should_receive is an expectation of something that happens in the future. Reverse these two lines:
get action, :id => 1
ability.should_receive(:can?)
so you have this:
ability.should_receive(:can?)
get action, :id => 1