1

Я пытаюсь проверить действия в спецификации контроллера, но по какой-то причине я получаю ошибки маршрутов без ошибок. Что мне делать, чтобы маршрут работал?rails + спецификация контроллера rspec с полиморфной ассоциацией

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 

ответ

2

Использование маршрута 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 всегда будет смотреть на 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 

Другой вариант, который я предпочитаю использовать запрос спецификации вместо:

require 'rails_helper' 
RSpec.describe "Comments", type: :request do 

    RSpec.shared_example "has nested comments" do 
    let(:path) { polymorphic_path(commentable) + "/comments" } 
    let(:params) { attributes_for(:comment) } 

    describe "POST create" do 
     expect do 
     xhr :post, path, params 
     end.to change(commentable.comments, :count).by(1) 
    end 
    end 


    context "Posts" do 
    include_examples "has nested comments" do 
     let(:commentable) { create(:post) } 
    end 
    end 

    context "Products" do 
    include_examples "has nested comments" do 
     let(:commentable) { create(:product) } 
    end 
    end 
end 

Поскольку вы действительно отправляете HTTP-запрос вместо того, чтобы притворяться, они охватывают больше стека приложений. Однако это связано с небольшой ценой с точки зрения скорости тестирования. Оба shared_context и shared_examples - это две вещи, которые делают RSpec действительно потрясающим.

+0

max, u справа, мои маршруты могут использоваться без вложенности для обновления/уничтожения. Я обновил свой ответ с помощью моей установки действий. Я бы сохранил код создания комментария в 'CommentsController' вместо' Posts :: CommentsController', как вы можете видеть в обновленном вопросе. У меня также есть комментарии к продукту, так что этого достаточно, чтобы поместить 'set_post' или' set_product' в контроллеры модулей, и я могу сохранить основную часть действия create в 'CommentsController'. Как вы думаете? Можете ли вы рассказать мне, как я могу проверить это, если у меня есть половина кода здесь и половина? Btw. в dev env все работает нормально. –

+0

max, pls также см. Мой предыдущий комментарий. Таким образом, я просто вложил свой контроллер, как вы посоветовали для создания действия, и это сработало. THX –

+0

Приятно, что это сработало - см. Мое редактирование для некоторых советов о том, как его проверить. – max