There are only two actions accessible in the ProductsController:
# /config/routes.rb
RailsApp::Application.routes.draw do
resources :products, only: [:index, :show]
end
Tests are set up accordingly:
# /spec/controllers/products_controller_spec.rb
require 'spec_helper'
describe ProductsController do
before do
#product = Product.gen
end
describe "GET index" do
it "renders the index template" do
get :index
expect(response.status).to eq(200)
expect(response).to render_template(:index)
end
end
describe "GET show" do
it "renders the show template" do
get :show, id: #product.id
expect(response.status).to eq(200)
expect(response).to render_template(:show)
end
end
end
How would you test that the other CRUD actions are not accessible? This might change in the future so the tests will ensure any configuration change will be noticed.
I found the be_routable matcher which looks promising to cover the test case.
I recommend this post by Dave Newton which describes when and why to test controller actions.
Here is what I came up with:
context "as any user" do
describe "not routable actions" do
it "rejects routing for :new" do
expect(get: "/products/new").not_to be_routable
end
it "rejects routing for :create" do
expect(post: "/products").not_to be_routable
end
it "rejects routing for :edit" do
expect(get: "/products/#{#product.id}/edit").not_to be_routable
end
it "rejects routing for :update" do
expect(put: "/products/#{#product.id}").not_to be_routable
end
it "rejects routing for :destroy" do
expect(delete: "/products/#{#product.id}").not_to be_routable
end
end
end
However one test fails:
Failure/Error: expect(get: "/products/new").not_to be_routable
expected {:get=>"/products/new"} not to be routable,
but it routes to {:action=>"show", :controller=>"products", :id=>"new"}
Please feel free to add your own solution if you follow a totally different approach to test non-existing routes.
Related
I couldn't find a solution in the other relative questions, so I'm asking my own.
The problem is pretty straightforward. This is the error I'm getting:
Failure/Error: get 'api/v2/special_keys#show'
ActionController::UrlGenerationError:
No route matches {:action=>"api/v2/special_keys#show", :controller=>"api/v2/special_keys"}
This is my routes.rb:
resources :special_keys, only: [] do
collection do
get '', to: 'special_keys#show'
end
end
This is the output from rake routes:
GET /api/v2/special_keys(.:format) api/v2/special_keys#show {:format=>"json"}
And my spec:
require 'rails_helper'
describe Api::V2::SpecialKeysController do
describe 'GET #show' do
it 'gets the policy and signature' do
get '/api/v2/special_keys'
expect(response.status).to eql 200
end
end
end
Try to rewrite your test as:
require 'rails_helper'
describe Api::V2::SpecialKeysController do
describe 'GET #show' do
it 'gets the policy and signature' do
get '/api/v2/special_keys', {format: :json}
expect(response.status).to eql 200
end
end
end
Try:
resource :special_keys, only: [:show]
The singular tells the app, that there is only one. So it will only generate a show action that needs no id and no indexaction at all.
I'm getting the following error in RSpec when running my schools_controller_spec.rb test:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"schools"}
What's puzzling me is that I have the routes configured, and the action defined in the appropriate controller. I'm not getting this error for other tests in the spec, such as 'GET #index', etc. Running Rails 4.2 with RSpec/Capybara.
Here's the routes.rb:
Rails.application.routes.draw do
root to: 'pages#home', id: 'home'
resources :users
resources :schools
resource :session, only: [:new, :create, :destroy]
match '/home', to: 'pages#home', via: 'get', as: 'home_page'
end
rake routes returns:
schools GET /schools(.:format) schools#index
POST /schools(.:format) schools#create
new_school GET /schools/new(.:format) schools#new
edit_school GET /schools/:id/edit(.:format) schools#edit
school GET /schools/:id(.:format) schools#show
PATCH /schools/:id(.:format) schools#update
PUT /schools/:id(.:format) schools#update
DELETE /schools/:id(.:format) schools#destroy
There's the route defined on the fifth line, as schools#show.
The schools_controller.rb:
class SchoolsController < ApplicationController
before_action :require_signin
before_filter :admin_only, except: :index, :show
def index
#schools = School.all
end
def show
# code pending
end
private
def admin_only
unless current_user.admin?
redirect_to :back, alert: "Access denied."
end
end
end
The link to the individual school seems to be properly defined in the view helper (_school.html.haml):
%li#schools
= link_to school.name, school
= school.short_name
= school.city
= school.state
and looking at the front-end HTML confirms it's working correctly. I can see, for example: Community College of the Air Force. When I click that link the page shows the following in the debug dump:
--- !ruby/hash:ActionController::Parameters
controller: schools
action: show
id: '1'
Finally, for good measure, here's the spec file (schools_controller_spec.rb):
require 'rails_helper'
describe SchoolsController, type: :controller do
# specs omitted for other actions
describe 'GET #show' do
context "when not signed in" do
it "returns a 302 redirect code" do
get :show
expect(response.status).to eq 302
end
it "redirects to the signin page" do
get :show
expect(response).to redirect_to new_session_path
end
end
context "when signed in as user" do
before :each do
#user = double(:user)
allow(controller).to receive(:current_user).and_return #user
#school = create(:school)
end
it "assigns the school to the #school variable" do
get :show
expect(assigns(:school)).to eq #school
end
end
end
end
The route appears in rake routes. The method is defined in the appropriate controller. There don't appear to be any silly naming errors (e.g. plural/singular). The spec doesn't appear to have any issues routing GET #index or other routes for example. Everything works exactly as expected in the browser.
So why do I keep getting the "no route matches" error when I run my controller spec?
This is because the show action is expecting an id which you currently aren't passing. Replace:
get :show
With this:
get :show, id: school.id
The above assumes you have a school variable, perhaps a let in a before block?
let(:school) { create(:school) }
I'm having controller test with devise, but it always fail because assigns always return nil, please help to find where the problem is, thanks a million!
posts_controller_spec.rb:
RSpec.describe PostsController, :type => :controller do
describe "with valid session" do
describe "GET index" do
it "assigns all posts as #posts" do
sign_in :admin, #user
post = create(:post)
get :index, {}
expect(assigns(:posts)).to eq([post])
end
end
end
...
end
posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
...
end
I've included devise test helpers in spec/rails_helper.rb
config.include Devise::TestHelpers, type: :controller
In my case, post is scoped under admin, not sure if that makes difference (functional test doesn't get through routes?), so I just include my routes.rb here
routes.rb:
Rails.application.routes.draw do
root to: 'home#index'
get 'admin', to: 'admin#index'
devise_for :users
scope '/admin' do
resources :posts
end
end
And finally, the output from rspec:
1) PostsController with valid session GET index assigns all posts as #posts
Failure/Error: expect(assigns(:posts)).to eq([post])
expected: [#<Post id: 57, title: "MyText", body: "MyText", image_url: "MyString", created_at: "2014-09-02 14:36:01", updated_at: "2014-09-02 14:36:01", user_id: 1>]
got: nil
(compared using ==)
# ./spec/controllers/posts_controller_spec.rb:53:in `block (4 levels) in <top (required)>'
I've read this thread rspec test of my controller returns nil (+factory girl) , and followed the suggestion to change get :index to controller.index . The suggestion is that if that passes the test then it's a routing problem. It does pass the test, but I still have no idea where the routing problem is, and why the get :index is not working...
It's just a small mistake: create an user before using devise sign_in
RSpec.describe PostsController, :type => :controller do
describe "with valid session" do
let (:user) { create(:user) }
describe "GET index" do
it "assigns all posts as #posts" do
sign_in user
post = create(:post)
get :index, {}
expect(assigns(:posts)).to eq([post])
end
...
end
end
end
I am using Rails 4.0.8. When I ran bundle exec rspec spec/, here's the error I got from the tutorial (http://www.railstutorial.org/book/filling_in_the_layout):
Pending: StaticPagesHelper add some examples to (or delete)
/Users/Desktop/sample_app/spec/helpers/static_pages_helper_spec.rb
# No reason given
# ./spec/helpers/static_pages_helper_spec.rb:14
Failures:
1) StaticPagesController GET '...' returns http success
Failure/Error: get '...'
ActionController::UrlGenerationError:
No route matches {:action=>"...", :controller=>"static_pages"}
# ./spec/controllers/static_pages_controller_spec.rb:7:in `block (3 levels) in '
Finished in 0.20849 seconds 19 examples, 1 failure, 1 pending
Failed examples:
rspec ./spec/controllers/static_pages_controller_spec.rb:6 #
StaticPagesController GET '...' returns http success
Here is my route.rb file:
SampleApp::Application.routes.draw do
resources :users
root to: 'static_pages#home'
match '/signup', to: 'users#new', via: 'get'
match '/help', to: 'static_pages#help', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
end
Here is my static_pages_helper_spec.rb file:
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the StaticPagesHelper. For example:
#
# describe StaticPagesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end describe StaticPagesHelper do pending "add some examples to (or delete) #{__FILE__}" end
Here is my static_pages_controller_spec.rb file:
require 'spec_helper'
describe StaticPagesController do
describe "GET '...'" do
it "returns http success" do
get '...'
response.should be_success
end
end
end
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
def about
end
def contact
end
end
I wonder whether I'm getting the error because my version is not compatible with his tutorial. Should I be looking at this tutorial instead http://rails-3-2.railstutorial.org/book/filling_in_the_layout#sec-rails_routes?
Your static_pages_controller_spec has
describe "GET '...'" do
it "returns http success" do
get '...'
response.should be_success
end
end
The "..." is nonsense in this context and the tutorial probably included it as a "specimen" entry.
Comment out the above lines, or replace "..." with a real method in your static_pages_controller.rb. You probably have an index method so you could do...
describe "GET index" do
it "returns http success" do
get :index
response.should be_success
end
end
Cheers
I have simplified an issue I am having to the following
I have a route that looks like this
namespace :client do
resources :thing, :only => [:index]
end
and the simple rspec test
describe Client::ThingController do
describe "GET 'index'" do
it "returns http success" do
get 'index'
response.should be_success
end
end
end
However what I'd like to do is alter the url I use to access the resource from
/client/thing.json
to
/api/client/v1/thing.json
1) How do I update my routes and rspec?
If I then wanted to parameterise the uri such that I could extract the api_version
/api/client/[api_version]/thing.json
2) How would this effect my routes and simple rspec test?
I'd previously tried what #Mori had suggested before I had posted but without having the multiple routes as proposed, I should have mentioned that in the original post.
What I eventually got that worked is:
1) In routes.rb I added
match "api/client/:api_version/thing" => 'client/thing#index'
NOTE: The missing leading '/' ie match "/api/... => match "api/... it seems to make all the difference to rspec.
2) And in my rspec thing_controller_spec.rb I now have this
describe Client::ThingController do
describe "GET 'index'" do
it "returns http success" do
get 'index', :api_version => 'v1'
response.should be_success
end
end
end
I was close before but it was the leading '/' in routes.rb that broke me even though I could navigate to the url in a browser.