My routes looks like this
resources :stores, :except => [:destroy] do
resources :toys, :member => {:destroy => :delete}
end
my objects controller spec look like this
require 'spec_helper'
describe ToysController do
describe "GET index" do
it "assigns all toys as #toys" do
toy11 = Factory(:toy, :is_shiny => true)
toy12 = Factory(:toy,:is_shiny => false)
get :index
assigns(:toys).should eq([toy12,toy11 ])
end
end
end
end
I got the following error
Failure/Error: get :index
ActionController::RoutingError:
No route matches {:controller=>"toys"}
Since the toys resource is nested under stores resources its not able to get toys_path route so i think so the spec is failing.
How do i pass the spec?
Thanks
The error is due to not sending store_id to tyos index.
Had i sent
:store_id => #store.id in get :index
it would have passed.
Related
I have a Queues controller and and QueueItems controller in my rails
application. In routes I have defined as below
match 'queues/:queue_id/next', :to=> 'queueitems#next'
In my QueueItems Controller I have a next action and it assigns an
instance variable.
def next
#queue = "Regular"
#other stuffs related to regular
end
How do I test this in Rspec. I am pretty very new to Rspec. Please help.
I tried like the below
describe QueuesController do
describe "next " do
it "routes /queues/:queue_id/next" do
{ :get => "/queues/regular_queue/next" }.should route_to(
:controller => "queue_items",
:action => "next",
:queue_id => "regular_queue",
:format => "json"
)
assigns(:queue).should_not be_nil
expect(response).to be_success
end
end
But it is not at all coming inside my next action in controller.
Update #2
spec/controllers/queue_items_controller_spec.rb
require 'spec_helper'
describe QueueItemsController do
describe 'GET next' do
it 'assigns #queue' do
get :next, format: :json
expect(assigns[:queue]).to eq('regular')
end
end
end
queue_items_controller.rb
def next
puts "Inside next action..."
#queue = "regular"
end
routes.rb
get '/queues/:queue_id/next', :to => 'queue_items#next', :format=>'json'
rake routes
GET /queues/:queue_id/next(.:format) queue_items#next {:format=>"json"}
/queues/:queue_id/delete(.:format) queue_items#delete {:method=>:delete, :format=>"json"}
/queues/:queue_id/clear(.:format) queue_items#clear {:format=>"json"}
First of all change match to get or post - it is better to use exactly verb. Let's say it is get.
Action next is in QueueItemsController so test should be in this queue_items_controller_spec.rb file (and in folder spec/controlers).
and test might be similar to
describe QueueItemsController do
describe "GET next " do
it "responses json" do
get :next, format: :json
expect(response).to be_success
end
it "does not response html" do
get :next, format: :html
expect(response).not_to be_success # or define more exactly response
end
it 'assigns #queue' do
get :next, format: :json
expect(assigns[:queue]).to eq('Regular')
end
end
end
if you would like to test your routes you should follow this articles:
https://www.relishapp.com/rspec/rspec-rails/v/2-14/docs/routing-specs
https://www.relishapp.com/rspec/rspec-rails/docs/routing-specs/route-to-matcher
If in views/abouts/ I have "index.html.haml" and "history.html.haml".
How can I access to abouts#history which is a basic html page.
From log I get this error, I guess it is processing it as a show, what can I do?:
Processing by AboutsController#show as HTML
Parameters: {"id"=>"history"}
About Load (0.3ms) SELECT `abouts`.* FROM `abouts` WHERE (`abouts`.`id` = 0) LIMIT 1
ActiveRecord::RecordNotFound (Couldn't find About with ID=history):
routes.rb
scope() do
resources :abouts, :path => 'about-us' do
match 'about-us/history' => "about-us#history"
end
end
abouts_controller.rb
def history
respond_to do |format|
format.html
end
end
A few problems. First, you should be matching 'history' and not 'about-us/history' (the route is nested so the 'about-us/' part is automatically included). Second, you need to specify that the route should match the collection, not a member of the collection, with the :on => :collection option. Finally, you should be routing the match to 'abouts#history' and not 'about-us#history' (because the controller is named abouts regardless of what path string you use when routing).
So try this:
resources :abouts, :path => 'about-us' do
match 'history' => "abouts#history", :on => :collection
end
Also note that match will match all HTTP requests: POST as well as GET. I'd suggest using get rather than match, to narrow the HTTP request type to just GET requests:
resources :abouts, :path => 'about-us' do
get 'history' => "abouts#history", :on => :collection
end
Hope that helps.
I try to test one of my REST api controllers which is placed at "controllers/api/v1/bookings_controller.rb". The controller only responds_to json at the moment as you can see here:
class Api::V1::BookingsController < ApplicationController
respond_to :json
before_filter :authenticate_user!
before_filter :get_user
def create
...
end
end
My functional test is located at "test/functional/api/v1/bookings_controller_test.rb" and looks like following:
require 'test_helper'
class Api::V1::BookingsControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup do
#booking = bookings(:one)
#user = users(:one)
sign_in #user
end
test "should return a bad request" do
post :create, :booking => { }, :format => 'json'
assert_response :bad_request
end
end
The post path for creating a booking looks like this (and works, tested with HTTP Client):
api_v1_user_bookings
GET /api/v1/users/:user_id/bookings(.:format) api/v1/bookings#index
POST /api/v1/users/:user_id/bookings(.:format) api/v1/bookings#create
However when I run the test it seems that it uses some default route (see error message below), how can i specify the correct route in my test? Or is there some other mistake I do not see here?
test_should_return_a_bad_request(Api::V1::BookingsControllerTest):
ActionController::RoutingError: No route matches {:booking=>{}, :format=>"js
on", :controller=>"api/v1/bookings", :action=>"create"}
Your route expects a user_id parameter. Add it to your post:
post :create, :user_id => #user.id, :booking => {}, :format => :json
Ok, I think I figured it out now. I just had to add the user-id, otherwise it seems that rails does not select the right route. So the correct test method looks like this:
test "should return a bad request" do
post :create, :user_id => #user.id, :booking => { }, :format => 'json'
assert_response :bad_request
end
Note: As per RafaeldeF.Ferreira's suggestion, this question has been heavily edited since its original form.
My JSON-based app needs to return something sensible when given a bad route. We already know that the following rescue_from ActionController::RoutingError doesn't work in Rails 3.1 and 3.2:
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from ActionController::RoutingError, :with => :not_found
...
end
(This is well documented in https://github.com/rails/rails/issues/671.) So I implemented what José Valim describes in this blog entry (item 3), and details are provided below.
But testing it has been problematic. This controller rspec test:
# file: spec/controllers/errors_controller.rb
require 'spec_helper'
require 'status_codes'
describe ErrorsController do
it "returns not_found status" do
get :not_found
response.should be(StatusCodes::NOT_FOUND)
end
end
fails with:
ActionController::RoutingError: No route matches {:format=>"json", :controller=>"sites", :action=>"update"}
Yet this integration test calls ErrorsController#not_found and succeeds:
# file: spec/requests/errors_spec.rb
require 'spec_helper'
require 'status_codes'
describe 'errors service' do
before(:each) do
#client = FactoryGirl.create(:client)
end
it "should catch routing error and return not_found" do
get "/v1/clients/login.json?id=#{#client.handle}&password=#{#client.password}"
response.status.should be(StatusCodes::OK)
post "/v1/sites/impossiblepaththatdoesnotexist"
response.status.should be(StatusCodes::NOT_FOUND)
end
end
So: Is there a way to test the 'catch all route' using ordinary controller tests?
implementation details
If you want to see the implementation, here are the relevant code snippets
# config/routes.rb
MyApp::Application.routes.draw do
... all other routes above here.
root :to => "pages#home"
match "/404", :to => "errors#not_found"
end
# config/application.rb
module MyApp
class Application < Rails::Application
config.exceptions_app = self.routes
...
end
end
# config/environments/test.rb
MyApp::Application.configure do
...
config.consider_all_requests_local = false
...
end
# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
def not_found
render :json => {:errors => ["Resource not found"]}, :status => :not_found
end
end
I have a set of API routes in rails as follows
namespace "api" do
namespace "v1" do
resources :users do
resources :posts
resources :likes
...
end
end
end
So far, so good. I can GET /api/v1/users/fred_flintstone and retrieve all of the information for that user.
What I would like to do now is add the concept of "me" (ala facebook) such that if the user is authenticated (fred_flintstone), I can also do the following
GET /api/v1/me
GET /api/v1/me/posts
...
I require both sets of routes. So I want to achieve the same results either using GET /api/v1/me/posts OR GET /api/v1/users/fred_flintstone/posts.
I've been through the route tutorial and have googled so a pointer would be as much appreciated as a direct answer.
EDIT:
What I've done that has worked is pretty hacky. I've created a second set of entries in the routes table using a scope:
scope "/api/v1/me", :defaults => {:format => 'json'}, :as => 'me' do
resources :posts, :controller => 'api/v1/users/posts'
resources :likes, :controller => 'api/v1/users/likes'
...
end
And then I added a set_user method that tests for the presence of params[:user_id]. I'm really looking for a way to DRY this up.
What about leaving the routes the way they are in your post, and just solving this inside the controller?
Heres a before_filter that you could apply to all of the routes you have which pull a User from a :user_id.
# Set the #user variable from the current url;
# Either by looking up params[:user_id] or
# by assigning current_user if params[:user_id] = 'me'
def user_from_user_id
if params[:user_id] == 'me' && current_user
#user = current_user
else
#user = User.find_by_user_id params[:user_id]
end
raise ActiveRecord::RecordNotFound unless #user
end
Then in your controller functions you can just use the #user variable without having to worry about whether the user passed a user_id, or me.
Hope that helps! :)
EDIT:
Lemme take another shot, given your comments.
How about a function that lists all the resources you wish to access via both the standard routes and the /me route. Then you can just use the function in both the namespaces you require.
routes.rb
# Resources for users, and for "/me/resource"
def user_resources
resources :posts
resources :likes
...
end
namespace 'api' do
namespace 'v1' do
resources :users do
user_resources
end
end
end
scope '/api/v1/:user_id', :constraints => { :user_id => 'me' },
:defaults => {:format => 'json'}, :as => 'me' do
user_resources
end
# We're still missing the plain "/me" route, for getting
# and updating, so hand code those in
match '/api/v1/:id' => 'users#show', :via => :get,
:constraints => { :id => 'me' }
match '/api/v1/:id' => 'users#update', :via => :put,
:constraints => { :id => 'me' }