Search code examples
ruby-on-rails-3rspecnested-routes

View's testing indicates 'No route matches' for nested resource


I have a Training model that's nested under a devise User model.

/config/routes.rb

devise_for :users, path: 'u'

resources :users, only: ['index','show'], shallow: true do
  resources :trainings
end

> rake routes

        new_user_session GET    /u/sign_in(.:format)                    {:action=>"new", :controller=>"devise/sessions"}
            user_session POST   /u/sign_in(.:format)                    {:action=>"create", :controller=>"devise/sessions"}
    destroy_user_session DELETE /u/sign_out(.:format)                   {:action=>"destroy", :controller=>"devise/sessions"}
           user_password POST   /u/password(.:format)                   {:action=>"create", :controller=>"devise/passwords"}
       new_user_password GET    /u/password/new(.:format)               {:action=>"new", :controller=>"devise/passwords"}
      edit_user_password GET    /u/password/edit(.:format)              {:action=>"edit", :controller=>"devise/passwords"}
                         PUT    /u/password(.:format)                   {:action=>"update", :controller=>"devise/passwords"}
cancel_user_registration GET    /u/cancel(.:format)                     {:action=>"cancel", :controller=>"devise/registrations"}
       user_registration POST   /u(.:format)                            {:action=>"create", :controller=>"devise/registrations"}
   new_user_registration GET    /u/sign_up(.:format)                    {:action=>"new", :controller=>"devise/registrations"}
  edit_user_registration GET    /u/edit(.:format)                       {:action=>"edit", :controller=>"devise/registrations"}
                         PUT    /u(.:format)                            {:action=>"update", :controller=>"devise/registrations"}
                         DELETE /u(.:format)                            {:action=>"destroy", :controller=>"devise/registrations"}
          user_trainings GET    /users/:user_id/trainings(.:format)     {:action=>"index", :controller=>"trainings"}
                         POST   /users/:user_id/trainings(.:format)     {:action=>"create", :controller=>"trainings"}
       new_user_training GET    /users/:user_id/trainings/new(.:format) {:action=>"new", :controller=>"trainings"}
           edit_training GET    /trainings/:id/edit(.:format)           {:action=>"edit", :controller=>"trainings"}
                training GET    /trainings/:id(.:format)                {:action=>"show", :controller=>"trainings"}
                         PUT    /trainings/:id(.:format)                {:action=>"update", :controller=>"trainings"}
                         DELETE /trainings/:id(.:format)                {:action=>"destroy", :controller=>"trainings"}
                   users GET    /users(.:format)                        {:action=>"index", :controller=>"users"}
                    user GET    /users/:id(.:format)                    {:action=>"show", :controller=>"users"}
                    root        /                                       {:controller=>"team", :action=>"index"}
                   teams GET    /teams(.:format)                        {:action=>"index", :controller=>"teams"}
                         POST   /teams(.:format)                        {:action=>"create", :controller=>"teams"}
                new_team GET    /teams/new(.:format)                    {:action=>"new", :controller=>"teams"}
               edit_team GET    /teams/:id/edit(.:format)               {:action=>"edit", :controller=>"teams"}
                    team GET    /teams/:id(.:format)                    {:action=>"show", :controller=>"teams"}
                         PUT    /teams/:id(.:format)                    {:action=>"update", :controller=>"teams"}
                         DELETE /teams/:id(.:format)                    {:action=>"destroy", :controller=>"teams"}

I'm having the following outputs when testing views:

Failures:

1) trainings/edit renders the edit training form
   Failure/Error: render
   ActionView::Template::Error:
     No route matches {:controller=>"trainings"}
   # ./app/views/trainings/edit.html.erb:6:in `_app_views_trainings_edit_html_erb___268980906337666865_45435220'
   # ./spec/views/trainings/edit.html.erb_spec.rb:12:in `block (2 levels) in <top (required)>'

2) trainings/index renders a list of trainings
   Failure/Error: render
   ActionView::Template::Error:
     No route matches {:action=>"new", :controller=>"trainings"}
   # ./app/views/trainings/index.html.erb:25:in `_app_views_trainings_index_html_erb___2979006340799666900_47907140'
   # ./spec/views/trainings/index.html.erb_spec.rb:18:in `block (2 levels) in <top (required)>'

3) trainings/show renders attributes in <p>
   Failure/Error: render
   ActionView::Template::Error:
     No route matches {:controller=>"trainings"}
   # ./app/views/trainings/show.html.erb:10:in `_app_views_trainings_show_html_erb__2049701071134787176_46584480'
   # ./spec/views/trainings/show.html.erb_spec.rb:12:in `block (2 levels) in <top (required)>'

4) trainings/new renders new training form
   Failure/Error: render
   ActionView::Template::Error:
     undefined method `trainings_path' for #<#<Class:0x00000004a54688>:0x0000000686bec8>
   # ./app/views/trainings/_form.html.erb:1:in `_app_views_trainings__form_html_erb__4221989810981841567_45123700'
   # ./app/views/trainings/new.html.erb:3:in `_app_views_trainings_new_html_erb__472203814357098165_54200400'
   # ./spec/views/trainings/new.html.erb_spec.rb:12:in `block (2 levels) in <top (required)>'

I won't post all tests and all layout files because it would get too big. Instead I'll post test and layout file for one view, and try to extend the correction to the others.

The test for training#index is

/spec/views/trainings/index.html.erb_spec.rb

require 'spec_helper'

describe "trainings/index" do
  before(:each) do
    assign(:trainings, [
      stub_model(Training,
        :user_id => 1,
        :training => "MyText"
      ),
      stub_model(Training,
        :user_id => 1,
        :training => "MyText"
      )
    ])
  end

  it "renders a list of trainings" do
    render
    # Run the generator again with the --webrat flag if you want to use webrat matchers
    assert_select "tr>td", :text => 1.to_s, :count => 2
    assert_select "tr>td", :text => "MyText".to_s, :count => 2
  end
end

/app/views/index.html.rb

<h1>Listing trainings</h1>

<table>
  <tr>
    <th>User</th>
    <th>Training</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @trainings.each do |training| %>
  <tr>
    <td><%= training.user_id %></td>
    <td><%= training.training %></td>
    <td><%= link_to 'Show', training %></td>
    <td><%= link_to 'Edit', edit_training_path(training) %></td>
    <td><%= link_to 'Destroy', training, confirm: 'Are you sure?', method: :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New Training', new_user_training_path(@user) %>

It indicates that routes doens't match, but they are there. Why is it happening?!


Solution

  • Your index view has new_user_training_path(@user), but your index spec is not creating @user. Just like you created @trainings you also need to create @user in your view spec, something like:

      before(:each) do
        assign(:user, 
          stub_model(User,
            :id => 1
          )
        )
        # ...
      end
    

    Also, the ActionView::Template::Error: undefined method 'trainings_path' for #<#<Class:...>> error is fixed by changing your new view from form_for @training to form_for [@user, @training] (nested).