How to write Spec tests for routes - Rails 4 - ruby-on-rails

I'm trying to write a test for SessionsController and I wrote the following:
I'm using Spec 3.3
RSpec.describe SessionsController, type: :controller do
describe SessionsController do
describe "POST create" do
it "sign in should have a valid route" do
post('/api/signin').should route_to('api/sessions#create')
end
end
end
end
This app is to work mostly as an API, so for now, there's no need for views.
In my routes I have the following:
match '/api/signin', to: 'api/sessions#create',
Yet the test is not passing.
Any suggestions?
EDIT: The errors:
rspec ./spec/controllers/sessions_controller_spec.rb:27 # SessionsController SessionsController POST create sign in should have a valid route
rspec ./spec/controllers/sessions_controller_spec.rb:31 # SessionsController SessionsController POST create creates a new session
EDIT2: Added full test code

You must specify type: :routing and use assert_routing which has the benefice to test your route in 2 ways (route generation and route matching)
I make my answer general, so other people can take info from it, please adapt to your case
describe MyController, type: :routing do
it 'routing' do
# This is optional, but also a good reminder to tell me when I add a route
# and forgot to update my specs.
# Please see bellow for the helper definition
expect(number_of_routes_for('my_controller')).to eq(8)
# Then test for the routes, one by one
assert_routing({method: :get, path: '/my_controller'}, {controller: 'my_controller', action: 'index'})
assert_routing({method: :get, path: '/my_controller/1'}, {controller: 'my_controller', action: 'show', id: '1'})
# ... And so on, for each route
end
end
Note: If get errors with assert_routing (I guess it will be the case with match, but I can't remember) then have a look at assert_generates and assert_recognizes
And the number_of_routes_for helper
def number_of_routes_for(controller)
Rails.application.routes.routes.to_a.select{ |r| r.defaults[:controller] == controller }.count
end

Related

Rspec test to request controller action but seems action wasn't executed

I am new to Rails and Rspec and trying to write spec to test one non RESTful controller action.
Spec code:
require 'rails_helper'
RSpec.describe ReportsController, type: :controller do
describe "GET /full_members" do
context "when current account doesn't have contacts" do
let!(:current_account) { create(:account) }
it "hit full_members action" do
expect(get: '/reports/full_members').to be_routable
expect(:get => "/reports/full_members").to route_to(
controller: "reports",
action: "full_members")
get :full_members
end
end
end
end
Controller code:
class ReportsController < ApplicationController
def full_members
require "pry"
binding.pry
set_select_options_full_members
#members = #current_account.contacts
end
...
I was expecting to execute spec and stop at the breaking point pry. But the actual result is spec passed and pry didn't work.
Finished in 0.17359 seconds (files took 1.16 seconds to load)
1 example, 0 failures
From rails routes I can see the route exists(the spec first two assertions also prove that)
~ยป rails routes | ag full_members
>> full_members_reports GET /reports/full_members(.:format)
My routes.rb
resources :reports, only: [:index] do
collection do
get 'full_members'
end
end
Can not figure out why the pry didn't work in controller action. Can anyone help with that? Thanks

Why am I getting ActionController::UrlGenerationError: "No route matches" in my controller spec?

I have a controller spec looking like this:
# config_controller_spec.rb
require "spec_helper"
describe Api::V4::ConfigController, type: :controller do
let(:parsed_response) { response.body.to_json }
describe 'GET app_config' do
it "renders successfully" do
get :app_config
expect(response).to be_success
expect(parsed_response).to eq("{key: val}")
end
end
end
When I run it however, I get:
ActionController::UrlGenerationError:
No route matches {:action=>"app_config", :controller=>"api/v4/config"}
I don't under stand why. I googled around and figured that if I add: use_route: :config to the get call like so: get :app_config, use_route: :config, then it works for some reason, though I don't understand why? But when appending that, I get the following deprecation error:
DEPRECATION WARNING: Passing the `use_route` option in functional tests are deprecated. Support for this option in the `process` method (and the related `get`, `head`, `post`, `patch`, `put` and `delete` helpers) will be removed in the next version without replacement.
Functional tests are essentially unit tests for controllers and they should not require knowledge to how the application's routes are configured. Instead, you should explicitly pass the appropiate params to the `process` method.
Previously the engines guide also contained an incorrect example that recommended using this option to test an engine's controllers within the dummy application.
That recommendation was incorrect and has since been corrected.
Instead, you should override the `#routes` variable in the test case with `Foo::Engine.routes`. See the updated engines guide for details.
Here is my controller:
# config_controller.rb
class Api::V4::ConfigController < Api::V4::BaseController
def app_config
render json: Api::V6::Config.app_config, root: false
end
end
And routes:
# routes.rb
MyApp::Application.routes.draw do
constraints subdomain: /\Awww\b/ do
namespace :api, defaults: {format: 'json'} do
get 'app_config' => 'config#app_config'
end
end
end
Use a request spec instead of a controller spec:
describe "Api V4 Configuration", type: :request do
let(:json) { JSON.parse(response.body) }
subject { response }
describe 'GET app_config' do
before { get "/api/v4/app_config" }
it { should be_successful }
it "has the correct contents" do
expect(json).to include(foo: "bar")
end
end
end
One of biggest changes with Rails 5 was the depreciation of ActionController::TestCase (which RSpec controller specs wrap) in favor of integration tests. Thus using request specs is a more future proof solution - using less abstraction also means that your specs will cover routing properly as well.
Also you don't seem to be nesting your routes properly:
# routes.rb
MyApp::Application.routes.draw do
constraints subdomain: /\Awww\b/ do
namespace :api, defaults: {format: 'json'} do
namespace :v4 do
get 'app_config' => 'config#app_config'
end
end
end
end
See:
Replacing RSpec controller specs

Can't get routes {MyEngine::Engine.routes} to work with shared examples using rspec-rails 3.1.0 and Rails 4.2.0

I am trying to update an engine I wrote in rails 4.1 to rails 4.2. This introduced a deprecation warning around using :use_route in my controller specs.
I have removed every place where I am using :use_route and have replaced it with putting routes {MyEngine::Engine.routes} at the top of the controller spec. This has worked as expected for all of the tests in the controller spec except for the ones that deal with shared examples.
None of my shared examples pass and I get the following:
Failure/Error: action
ActionController::UrlGenerationError:
No route matches {:action=>"destroy", :controller=>"foo/roles", :id=>"1"}
I would like to know how to get my shared examples to pass given this way of adding routes to my controller specs.
An example of one of the specs I have is:
spec/controllers/roles_controller_spec.rb
require 'rails_helper'
module Foo
describe RolesController do
routes {Foo::Engine.routes}
before { sign_in_user FactoryGirl.create(:user, admin: true)}
it_behaves_like 'requires admin privilege' do
let(:action) {get :show, id: role.id}
end
end
end
spec/support/shared_examples.rb
shared_examples "requires admin privilege" do
routes { Foo::Engine.routes }
it 'redirects to main app root if user is not an admin' do
session[:user_id] = FactoryGirl.create(:user)
action
expect(response).to redirect_to main_app.root_path
end
end
Any ideas or help is greatly appreciated.
Thanks

Specs for controller inside a module (versionist)

I'm creating an API in Rails and I use versionist to handle versions. I want to test API controllers, but I'm unable to create a valid request.
My controller:
class Api::V1::ItemsController < Api::V1::BaseController
def index
render json:'anything'
end
end
My spec:
describe Api::V1::ItemsController do
describe "#create" do
it "shows items" do
get :index, format: :json
end
end
end
routes.rb:
scope '/api' do
api_version(:module => "Api::V1", :path => {:value => "v1"}, :default => true) do
resources :items
end
end
The test doesn't check anything. Still, it raises an error:
Failure/Error: get :index, format: :json
ActionController::RoutingError:
No route matches {:format=>:json, :controller=>"api/v1/items", :action=>"index"}
I suppose that there is something wrong with the :controller key in the request, but I don't know how to fix it...
I was able to reproduce this locally. You need to move this to a request spec instead of a controller spec for this to work:
# spec/requests/api/v1/items_controller_spec.rb
describe Api::V1::ItemsController do
describe "#index" do
it "shows items" do
get '/api/v1/items.json'
# assert something
end
end
end
The versionist documentation says you need to do this when using the HTTP header or request parameter versioning strategies (https://github.com/bploetz/versionist#a-note-about-testing-when-using-the-http-header-or-request-parameter-strategies) but that's clearly not the case here. I'll file an issue to get this clarified in the documentation that you need to do this for all versioning strategies.

How to use rspec to test named routes?

Given I have a named route:
map.some_route '/some_routes/:id', :controller => 'some', :action => 'other'
How do I use the routing spec file 'spec/routing/some_routing_spec.rb' to test for that named route?
I've tried this after the "describe SomeRouteController" block and it doesn't work, I get 'undefined method "helper":
describe SomeRouteHelper, 'some routes named routes' do
it 'should recognize some_route' do
helper.some_route_path(23).should == '/some_routes/23'
end
end
If this is in a controller spec, you can call the routing method directly, no helper needed.
describe SomeController do
it 'should recognize ma routes!' do
thing_path(23).should == '/things/23'
end
end
In RSpec-Rails 2.7+ you can create a spec/routing directory and put your routing specs in there. See the rspec-rails docs for more info.
there's a nice shoulda matcher for this too:
it { should route(:get, "/users/23").to(:action => "show", :id => 23)
more information on using shoulda matchers with rspec:
https://github.com/thoughtbot/shoulda-matchers
You can do this in your controller specs with the assert_routing method, like so:
describe UsersController do
it "should recognize a specific users#show route" do
assert_routing("/users/23", {:controller => "users", :action => "show", :id => 23})
end
end
More documentation is here.
This is how I write the specs using RSpec2, Shoulda, and Capybara. You would save this example file in #{Rails.root}/spec/routing/thingz_routing_spec.rb or my preference #{Rails.root}/spec/routing/thingz_controller_spec.rb
require "spec_helper"
describe ThingzController do
describe "routing" do
it "routes to #index" do
get("/thingz").should route_to("thingz#index")
end
end
end

Resources