尽管控制器中存在路由,但没有路由匹配 Rspec 中的删除
No route matches delete in Rspec despite route existing in controller
我正在测试一系列 API 调用,我收到一个错误,指出应用程序中不存在各种路由动词,尽管它们存在。
据我所知,销毁方法是存在的并且是允许的。问题与 PATCH
相同,但我只是将其限制为 DELETE
,因为我预计根本原因可能相同。
为了更正这个问题,我遗漏了什么?
错误:
6) /api/v1/users DELETE /destroy destroys the requested api/v1_user
Failure/Error: delete api_v1_users_url(user), headers: valid_headers, as: :json
ActionController::RoutingError:
No route matches [DELETE] "/api/v1/users.5e9047c21d41c8454835b38b"
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/debug_exceptions.rb:36:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/rack/logger.rb:38:in `call_app'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/rack/logger.rb:26:in `block in call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/tagged_logging.rb:80:in `block in tagged'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/tagged_logging.rb:28:in `tagged'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/tagged_logging.rb:80:in `tagged'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/rack/logger.rb:26:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/request_id.rb:27:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-2.2.2/lib/rack/runtime.rb:22:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/executor.rb:14:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/static.rb:126:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-2.2.2/lib/rack/sendfile.rb:110:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/host_authorization.rb:77:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/engine.rb:526:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-test-1.1.0/lib/rack/mock_session.rb:29:in `request'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-test-1.1.0/lib/rack/test.rb:266:in `process_request'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-test-1.1.0/lib/rack/test.rb:119:in `request'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/testing/integration.rb:270:in `process'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/testing/integration.rb:42:in `delete'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/testing/integration.rb:357:in `block (2 levels) in <module:Runner>'
# ./spec/requests/api/v1/users_spec.rb:101:in `block (4 levels) in <top (required)>'
# ./spec/requests/api/v1/users_spec.rb:100:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:17:in `block (3 levels) in <top (required)>'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/database_cleaner-1.8.4/lib/database_cleaner/generic/base.rb:16:in `cleaning'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:87:in `block (2 levels) in cleaning'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:88:in `cleaning'
# ./spec/rails_helper.rb:16:in `block (2 levels) in <top (required)>'
Finished in 1.33 seconds (files took 3.57 seconds to load)
16 examples, 6 failures
Failed examples:
rspec ./spec/requests/api/v1/users_spec.rb:47 # /api/v1/users POST /create with invalid parameters does not create a new user
rspec ./spec/requests/api/v1/users_spec.rb:54 # /api/v1/users POST /create with invalid parameters renders a JSON response with errors for the new api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:69 # /api/v1/users PATCH /update with valid parameters updates the requested api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:77 # /api/v1/users PATCH /update with valid parameters renders a JSON response with the api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:87 # /api/v1/users PATCH /update with invalid parameters renders a JSON response with errors for the api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:98 # /api/v1/users DELETE /destroy destroys the requested api/v1_user
测试
describe "DELETE /destroy" do
it "destroys the requested api/v1_user" do
user = Api::V1::User.create! valid_attributes
expect {
delete api_v1_users_url(user), headers: valid_headers, as: :json
}.to change(Api::V1::User, :count).by(-1)
end
end
users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
# GET /api/v1/users
def index
@users = Api::V1::User.all
render json: @users
end
# GET /api/v1/users/1
def show
render json: @user
end
# POST /api/v1/users
def create
@user = Api::V1::User.new(user_params)
if @user.save
render json: @user, status: :created, location: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /api/v1/users/1
def update
if @user.update(user_params)
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
# DELETE /api/v1/users/1
def destroy
@user.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = Api::V1::User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:first_name, :second_name, :username, :email, :password)
end
end
routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :users
end
end
end
也许试试
api_v1_user_path(user)
致那些为任何类似问题而苦苦挣扎的人。问题出在我的嵌套 /api/v1
文件夹结构上,正如上面 Max 所建议的那样,它导致了有点难看的 api_v1_users_url(user)
。这源于我使用脚手架而不是手动添加文件。
似乎rails 使用控制器的完整路径来构造对象和关联变量。
我重构了所有 class 声明和 rspec 文件路径,如下所示。
这似乎已经解决了评论中提到的初始代码味道以及初始问题。
➜ api git:(authentication) ✗ tree spec
spec
├── factories
│ └── users.rb
├── models
├── rails_helper.rb
├── requests
│ └── api
│ └── users_spec.rb
├── routing
│ └── users_routing_spec.rb
├── spec_helper.rb
└── support
└── factory_bot.rb
➜ api git:(authentication) ✗ tree app
app
├── channels
│ └── application_cable
│ ├── channel.rb
│ └── connection.rb
├── controllers
│ ├── application_controller.rb
│ ├── concerns
│ └── users_controller.rb
├── jobs
│ └── application_job.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── concerns
│ └── user.rb
└── views
└── layouts
├── mailer.html.erb
└── mailer.text.erb
更正后的 Rspec 测试现在通常如下所示:
describe "DELETE /destroy" do
it "destroys the requested user" do
user = User.create! valid_attributes
expect {
delete user_url(user),headers: valid_headers, as: :json
}.to change(User, :count).by(-1)
end
end
删除嵌套声明:
user = Api::V1::User.create! valid_attributes
我正在测试一系列 API 调用,我收到一个错误,指出应用程序中不存在各种路由动词,尽管它们存在。
据我所知,销毁方法是存在的并且是允许的。问题与 PATCH
相同,但我只是将其限制为 DELETE
,因为我预计根本原因可能相同。
为了更正这个问题,我遗漏了什么?
错误:
6) /api/v1/users DELETE /destroy destroys the requested api/v1_user
Failure/Error: delete api_v1_users_url(user), headers: valid_headers, as: :json
ActionController::RoutingError:
No route matches [DELETE] "/api/v1/users.5e9047c21d41c8454835b38b"
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/debug_exceptions.rb:36:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/rack/logger.rb:38:in `call_app'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/rack/logger.rb:26:in `block in call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/tagged_logging.rb:80:in `block in tagged'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/tagged_logging.rb:28:in `tagged'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/tagged_logging.rb:80:in `tagged'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/rack/logger.rb:26:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/request_id.rb:27:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-2.2.2/lib/rack/runtime.rb:22:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/activesupport-6.0.2.2/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/executor.rb:14:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/static.rb:126:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-2.2.2/lib/rack/sendfile.rb:110:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/middleware/host_authorization.rb:77:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/railties-6.0.2.2/lib/rails/engine.rb:526:in `call'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-test-1.1.0/lib/rack/mock_session.rb:29:in `request'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-test-1.1.0/lib/rack/test.rb:266:in `process_request'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/rack-test-1.1.0/lib/rack/test.rb:119:in `request'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/testing/integration.rb:270:in `process'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/testing/integration.rb:42:in `delete'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/actionpack-6.0.2.2/lib/action_dispatch/testing/integration.rb:357:in `block (2 levels) in <module:Runner>'
# ./spec/requests/api/v1/users_spec.rb:101:in `block (4 levels) in <top (required)>'
# ./spec/requests/api/v1/users_spec.rb:100:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:17:in `block (3 levels) in <top (required)>'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/database_cleaner-1.8.4/lib/database_cleaner/generic/base.rb:16:in `cleaning'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:87:in `block (2 levels) in cleaning'
# /home/etherk1ll/.rvm/gems/ruby-2.7.0/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:88:in `cleaning'
# ./spec/rails_helper.rb:16:in `block (2 levels) in <top (required)>'
Finished in 1.33 seconds (files took 3.57 seconds to load)
16 examples, 6 failures
Failed examples:
rspec ./spec/requests/api/v1/users_spec.rb:47 # /api/v1/users POST /create with invalid parameters does not create a new user
rspec ./spec/requests/api/v1/users_spec.rb:54 # /api/v1/users POST /create with invalid parameters renders a JSON response with errors for the new api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:69 # /api/v1/users PATCH /update with valid parameters updates the requested api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:77 # /api/v1/users PATCH /update with valid parameters renders a JSON response with the api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:87 # /api/v1/users PATCH /update with invalid parameters renders a JSON response with errors for the api/v1_user
rspec ./spec/requests/api/v1/users_spec.rb:98 # /api/v1/users DELETE /destroy destroys the requested api/v1_user
测试
describe "DELETE /destroy" do
it "destroys the requested api/v1_user" do
user = Api::V1::User.create! valid_attributes
expect {
delete api_v1_users_url(user), headers: valid_headers, as: :json
}.to change(Api::V1::User, :count).by(-1)
end
end
users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
# GET /api/v1/users
def index
@users = Api::V1::User.all
render json: @users
end
# GET /api/v1/users/1
def show
render json: @user
end
# POST /api/v1/users
def create
@user = Api::V1::User.new(user_params)
if @user.save
render json: @user, status: :created, location: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /api/v1/users/1
def update
if @user.update(user_params)
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
# DELETE /api/v1/users/1
def destroy
@user.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = Api::V1::User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:first_name, :second_name, :username, :email, :password)
end
end
routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :users
end
end
end
也许试试
api_v1_user_path(user)
致那些为任何类似问题而苦苦挣扎的人。问题出在我的嵌套 /api/v1
文件夹结构上,正如上面 Max 所建议的那样,它导致了有点难看的 api_v1_users_url(user)
。这源于我使用脚手架而不是手动添加文件。
似乎rails 使用控制器的完整路径来构造对象和关联变量。
我重构了所有 class 声明和 rspec 文件路径,如下所示。
这似乎已经解决了评论中提到的初始代码味道以及初始问题。
➜ api git:(authentication) ✗ tree spec
spec
├── factories
│ └── users.rb
├── models
├── rails_helper.rb
├── requests
│ └── api
│ └── users_spec.rb
├── routing
│ └── users_routing_spec.rb
├── spec_helper.rb
└── support
└── factory_bot.rb
➜ api git:(authentication) ✗ tree app
app
├── channels
│ └── application_cable
│ ├── channel.rb
│ └── connection.rb
├── controllers
│ ├── application_controller.rb
│ ├── concerns
│ └── users_controller.rb
├── jobs
│ └── application_job.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── concerns
│ └── user.rb
└── views
└── layouts
├── mailer.html.erb
└── mailer.text.erb
更正后的 Rspec 测试现在通常如下所示:
describe "DELETE /destroy" do
it "destroys the requested user" do
user = User.create! valid_attributes
expect {
delete user_url(user),headers: valid_headers, as: :json
}.to change(User, :count).by(-1)
end
end
删除嵌套声明:
user = Api::V1::User.create! valid_attributes