我试图在控制器规范中测试操作,但由于某种原因,我得到了无路由匹配错误。我该怎么做才能使路线有效?
ActionController::UrlGenerationError:
No route matches {:action=>"create", :comment=>{:body=>"Consectetur quo accusamus ea.",
:commentable=>"4"}, :controller=>"comments", :post_id=>"4"}
模型
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true, touch: true
class Post < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
路由
resources :posts do
resources :comments, only: [:create, :update, :destroy], module: :posts
end
controller_spec
describe "POST create" do
let!(:user) { create(:user) }
let!(:profile) { create(:profile, user: @user) }
let!(:commentable) { create(:post, user: @user) }
context "with valid attributes" do
subject(:create_action) { xhr :post, :create, post_id: commentable, comment: attributes_for(:comment, commentable: commentable, user: @user) }
it "saves the new task in the db" do
expect{ create_action }.to change{ Comment.count }.by(1)
end
...
修改
上面的controller_spec可以在spec/controllers/comments_controller_spec.rb
控制器/ comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
@comment = @commentable.comments.new(comment_params)
authorize @comment
@comment.user = current_user
if @comment.save
@comment.send_comment_creation_notification(@commentable)
respond_to :js
end
end
控制器/帖/ comments_controller.rb
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
@commentable = Post.find(params[:post_id])
end
答案 0 :(得分:3)
使用module: :posts
将路由到Posts::CommentsController#create
。
如果这不是您想要的,请删除模块选项。
否则,您需要确保控制器和规格都具有正确的类名。
class Posts::CommentsController
def create
end
end
RSpec.describe Posts::CommentsController do
# ...
end
另请注意,如果通常没有意义嵌套“个人行动”。对于资源。
相反,你可能想要声明这样的路线:
resources :comments, only: [:update, :destroy] # show, edit ...
resources :posts do
resources :comments, only: [:create], module: :posts # new, index
end
这给了你:
class CommentsController < ApplicationController
before_action :set_posts
# DELETE /comments/:id
def destroy
# ...
end
# PUT|PATCH /comments/:id
def update
end
end
class Posts::CommentsController < ApplicationController
# POST /posts/:post_id/comments
def create
# ...
end
end
请参阅Avoid Deeply Nested Routes in Rails以深入解释原因。
在这种情况下将控制器设置为使用继承是一个好主意 - 但是您无法通过控制器规范中的父create
类测试CommentsController
方法,因为RSpec将始终查看{尝试解决路线时{1}}。
相反,您可能想要使用共享示例:
described_class
# /spec/support/shared_examples/comments.rb
RSpec.shared_examples "nested comments controller" do |parameter|
describe "POST create" do
let!(:user) { create(:user) }
context "with valid attributes" do
subject(:create_action) { xhr :post, :create, post_id: commentable, comment: attributes_for(:comment, commentable: commentable, user: @user) }
it "saves the new task in the db" do
expect{ create_action }.to change{ Comment.count }.by(1)
end
end
end
end
require 'rails_helper'
require 'shared_examples/comments'
RSpec.describe Posts::CommentsController
# ...
include_examples "nested comments controller" do
let(:commentable) { create(:post, ...) }
end
end
我更喜欢的另一种选择是使用请求规范:
require 'rails_helper'
require 'shared_examples/comments'
RSpec.describe Products::CommentsController
# ...
include_examples "nested comments controller" do
let(:commentable) { create(:product, ...) }
end
end
由于您实际上是在发送HTTP请求而不是伪造它,因此它们覆盖了更多的应用程序堆栈。然而,这在测试速度方面具有很小的价格。 shared_context和shared_examples都是让RSpec真棒的两件事。