Michael
Michael

Reputation: 821

Route does not match in test but is recognized in application

I am following Michael Hartl's tutorial, chapter 10. Test UsersControllerTest#test_should_redirect_edit_when_logged_in_as_wrong_user fails when trying to do get edit_user_path(@user).

get edit_user_path(@user)
ActionController::UrlGenerationError: No route matches {:action=>"/users/762146111/edit", :controller=>"users"}
from /Users/cello/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/actionpack-5.1.4/lib/action_dispatch/journey/formatter.rb:55:in `generate' 

However:

Rails.application.routes.recognize_path '/users/762146111/edit', method: :get
=> {:controller=>"users", :action=>"edit", :id=>"762146111"}

Below is the code that might have a bug (Rails 5.1.4).

routes.rb

Rails.application.routes.draw do
  root 'static_pages#home'

  get  '/help', to: 'static_pages#help'
  get  '/about', to: 'static_pages#about'
  get  '/contact', to: 'static_pages#contact'
  get  '/signup', to: 'users#new'
  post 'signup', to: 'users#create'
  get '/login', to: 'sessions#new'
  post '/login', to: 'sessions#create'
  delete '/logout', to: 'sessions#destroy'
  get 'sessions/new'

  resources :users
end

users_controller_test.rb

require 'test_helper'

class UsersControllerTest < ActionController::TestCase
  def setup
    @user       = users(:michael)
    @other_user = users(:archer)
  end

  test 'should redirect edit when logged in as wrong user' do
    log_in_as(@other_user)
    get edit_user_path(@user)
    assert flash.empty?
    assert_redirected_to root_url
  end
end

users_controller.rb

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:edit, :update]
  before_action :correct_user,   only: [:edit, :update]

  def edit
    @user = User.find(params[:id])
  end

  private

  def logged_in_user
    unless logged_in?
      flash[:danger] = 'Please log in.'
      redirect_to login_url
    end
  end

  def correct_user
    @user = User.find(params[:id])
    redirect_to(root_url) unless current_user?(@user)
  end
end

Upvotes: 0

Views: 224

Answers (2)

matthewd
matthewd

Reputation: 4435

The tutorial is defining an Integration Test (inherits from ActionDispatch::IntegrationTest), whereas your above code is defining a Controller Test (inherits from ActionController::TestCase).

get :edit, ... is the correct syntax for a controller test, because it bypasses URL recognition and directly specifies the :action. This is confusing, and is one of several reasons that controller tests are now discouraged in favour of integration tests, which is probably what you want to create.

To do so, change:

class UsersControllerTest < ActionController::TestCase

to:

class UsersControllerTest < ActionDispatch::IntegrationTest

(Note the tutorial, somewhat confusingly, uses ActionDispatch::IntegrationTest as the base class both for tests it puts in tests/integration/ and those it puts in tests/controllers/.)

Upvotes: 1

Oleh Feilo
Oleh Feilo

Reputation: 84

You can not use direct url with 'get' method in spec.

In your spec instead of

get edit_user_path(@user)

use

get :edit, params: { id: @user.id }

the same with

patch user_path(@user)

use patch :update instead

Upvotes: 0

Related Questions