Adjusting rspec routing tests for nested routes - ruby-on-rails

When building my app, I generated scaffolds, which created standard Rspec tests. I'd like to use these tests for coverage, but they seem to be failing due to nested routes:
When I run the test, this is its feedback:
Failures:
1) ListItemsController routing routes to #index
Failure/Error: get("/list_items").should route_to("list_items#index")
No route matches "/list_items"
# ./spec/routing/list_items_routing_spec.rb:7:in `block (3 levels) in <top (required)>'
Finished in 0.25616 seconds
1 example, 1 failure
How do I tell Rspec that there are nested routes?
Here are the abridged files:
list_items_routing_spec.rb:
require "spec_helper"
describe ListItemsController do
describe "routing" do
it "routes to #index" do
get("/list_items").should route_to("list_items#index")
end
end
list_items_controller_spec.rb:
describe ListItemsController do
# This should return the minimal set of attributes required to create a valid
# ListItem. As you add validations to ListItem, be sure to
# adjust the attributes here as well.
let(:valid_attributes) { { "list_id" => "1", "project_id" => "1" } }
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# ListItemsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET index" do
it "assigns all list_items as #list_items" do
list_item = ListItem.create! valid_attributes
get :index, project_id: 2, {}, valid_session
assigns(:list_items).should eq([list_item])
end
end
routes.rb:
resources :projects do
member do
match "list_items"
end
end
Notes:
- I've tried changing the rpec tests themselves to include a project_id, and that didn't help.
- I'm using Factory Girl for fixture generation (not sure if this is relevant)
Thanks for your help!

First of all, run rake routes to see what routes exist.
According to what you have in your routes I would expect you have a ProjectsController that has an action list_items. This action would be available under /projects/:id/list_items.
Now I can only theorize about what really you want, but I will guess.
If you want /projects/:project_id/list_items to route to list_items#index you have to change your routes to:
resources :projects do
resources :list_items
end
You can confirm that by running rake routes.
Then fix the assertion in your routing spec:
get("/projects/23/list_items").should route_to("list_items#index", :project_id => "23")
Update for RSpec v2.14+ Expectations
expect(:get => "/projects/23/list_items").to route_to("list_items#index", :project_id => "23")

Related

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

RSpec: Controller spec with polymorphic resource, "No route matches" error

I'm learning RSpec by writing specs for an existing project. I'm having trouble with a controller spec for a polymorphic resource Notes. Virtually any other model can have a relationship with Notes like this: has_many :notes, as: :noteable
In addition, the app is multi-tenant, where each Account can have many Users. Each Account is accessed by :slug instead of :id in the URL. So my mulit-tenant, polymorphic routing looks like this:
# config/routes.rb
...
scope ':slug', module: 'accounts' do
...
resources :customers do
resources :notes
end
resources :products do
resources :notes
end
end
This results in routes like this for the :new action
new_customer_note GET /:slug/customers/:customer_id/notes/new(.:format) accounts/notes#new
new_product_note GET /:slug/products/:product_id/notes/new(.:format) accounts/notes#new
Now on to the testing problem. First, here's an example of how I test other non-polymorphic controllers, like invitations_controller:
# from spec/controllers/accounts/invitation_controller_spec.rb
require 'rails_helper'
describe Accounts::InvitationsController do
describe 'creating and sending invitation' do
before :each do
#owner = create(:user)
sign_in #owner
#account = create(:account, owner: #owner)
end
describe 'GET #new' do
it "assigns a new Invitation to #invitation" do
get :new, slug: #account.slug
expect(assigns(:invitation)).to be_a_new(Invitation)
end
end
...
end
When i try to use a similar approach to test the polymorphic notes_controller, I get confused :-)
# from spec/controllers/accounts/notes_controller_spec.rb
require 'rails_helper'
describe Accounts::NotesController do
before :each do
#owner = create(:user)
sign_in #owner
#account = create(:account, owner: #owner)
#noteable = create(:customer, account: #account)
end
describe 'GET #new' do
it 'assigns a new note to #note for the noteable object' do
get :new, slug: #account.slug, noteable: #noteable # no idea how to fix this :-)
expect(:note).to be_a_new(:note)
end
end
end
Here I'm just creating a Customer as #noteable in the before block, but it could just as well have been a Product. When I run rspec, I get this error:
No route matches {:action=>"new", :controller=>"accounts/notes", :noteable=>"1", :slug=>"nicolaswisozk"}
I see what the problem is, but i just can't figure out how to address the dynamic parts of the URL, like /products/ or /customers/.
Any help is appreciated :-)
UPDATE:
Changed the get :new line as requested below to
get :new, slug: #account.slug, customer_id: #noteable
and this causes the error
Failure/Error: expect(:note).to be_a_new(:note)
TypeError:
class or module required
# ./spec/controllers/accounts/notes_controller_spec.rb:16:in `block (3 levels) in <top (required)>'
Line 16 in the spec is:
expect(:note).to be_a_new(:note)
Could this be because the :new action in my notes_controller.rb is not just a #note = Note.new, but is initializing a new Note on a #noteable, like this?:
def new
#noteable = find_noteable
#note = #noteable.notes.new
end
Well the problem here should be that in this line
get :new, slug: #account.slug, noteable: #noteable
you are passing :noteable in params. But, you need to pass all the dynamic parts of the url instead to help rails match the routes. Here you need to pass :customer_id in params. Like this,
get :new, slug: #account.slug, customer_id: #noteable.id
Please let me know if this helps.

Multiple Rails engine rspec controller test not work

I have multiple Rails engines in my Rails 4 beta1 application. I'm installed rspec-rails gem to every engines. And I created my engine following command:
rails plugin new store_frontend --dummy-path=spec/dummy -d postgresql --skip-test-unit --mountable
In my engine's dummy application I configured database and routes. Here is example routes.rb file:
Rails.application.routes.draw do
mount StoreFrontend::Engine => "/store"
end
When I run rspec inside first engine I get following errors:
1) StoreAdmin::DashboardController GET 'index' returns http success
Failure/Error: get 'index'
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"store_admin/dashboard"}
# ./spec/controllers/store_admin/dashboard_controller_spec.rb:8:in `block (3 levels) in <module:StoreAdmin>'
And here is my controller test /It's generated from Rails/:
require 'spec_helper'
module StoreFrontend
describe HomeController do
describe "GET 'index'" do
it "returns http success" do
get 'index'
response.should be_success
end
end
end
end
It seems like controller test is not working. I have model tests and it's working fine. Any idea?
UPDATE 1:
My application structure:
bin/
config/
db/
lib/
log/
public/
tmp/
engine1/
engine2/
engine3/
The solution is very simple. Add use_route to your controller test. Here is the example.
module StoreFrontend
describe HomeController do
describe "GET 'index'" do
it "returns http success" do
get 'index', use_route: 'store_frontend' # in my case
response.should be_success
end
end
end
end
The configuration and spec you show are for StoreFrontend but the error is for StoreAdmin::DashboardController. So it seems like you are just confused about which engine you are testing and/or which engine is failing.
Of course the simple solution is to create the missing route {:action=>"index", :controller=>"store_admin/dashboard"}
In order to get the routing correct when testing Rails engine controllers with Rspec, I typically add the following code to my spec_helper.rb:
RSpec.configure do |config|
config.before(:each, :type => :controller) { #routes = YourEngineName::Engine.routes }
config.before(:each, :type => :routing) { #routes = YourEngineName::Engine.routes }
end

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.

Rspec test for the existence of an action is not working

i am working in Rspec of ROR..
I am trying to test my controllers using RSpec.i am having a Users controller with functions like new , tags, etc..
i created a file under spec/users_controller_spec.rb
and added the test cases as.
require 'spec_helper'
describe UsersController do
integrate_views
it "should use UsersController" do
controller.should be_an_instance_of(UsersController)
end
describe "GET 'new'" do
it "should be successful" do
get 'new'
response.should be_success
end
it "should have the title" do
get 'new'
response.should have_tag("title", "First app" )
end
end
end
which gets pass.
But when i add a test case for tags ..
like
describe "GET 'tags'" do
it "should be successful" do
get 'tags'
response.should be_success
end
end
this results in an error as
F...
1)
'UsersController GET 'tags' should be successful' FAILED
expected success? to return true, got false
why it is coming like this ?? i am very new to ROR and cant find the reason of why i am getting this error..
How to make this pass .
Also i tried the Url
http://localhost:3000/users/tags which is running for me .. But on testing using $spec spec/ i am getting the error ..
Your test may be failing for any number of reasons. Does the route require an ID in the parameter hash? Is the controller action redirecting? Is the controller raising an error?
You'll need to look at the controller code /and/or routes.rb to discover the cause of the failure. Take note of before filters in the controller, which may not allow the action to be reachable at all.
You need to add custom routes that are not within the default 7 routes. Assuming you have resources :users within your routes you will need to modify it. I'm also assuming that your tags route is unique to individual users.
resources :users do
member do
# creates /users/:user_id/tags
get :tags
end
end
And in your RSpec test you would call it like
describe '#tags' do
user = create :user
get :tags, user_id: user.id
end
If the route is not to be unique per user the other option is a collection route, something like:
resources :users do
collection do
# creates /users/tags
get :tags
end
end

Resources