RSpec控制器:redirect_to其他控制器

时间:2016-01-20 19:09:30

标签: ruby-on-rails ruby rspec

我在测试 CommentsController

时遇到了这个问题
  

失败/错误:redirect_to user_path(@ comment.user),注意:'你的   评论已成功添加!' ActionController的:: UrlGenerationError:   没有路由匹配{:action =>“show”,:controller =>“users”,:id => nil}   缺少必需的键:[:id]

这是我在控制器中的方法:

  def create 
    if params[:parent_id].to_i > 0
      parent = Comment.find_by_id(params[:comment].delete(:parent_id))
      @comment = parent.children.build(comment_params)
    else
      @comment = Comment.new(comment_params)
    end
    @comment.author_id = current_user.id
    if @comment.save
      redirect_to user_path(@comment.user), notice: 'Your comment was successfully added!'
    else
      redirect_to user_path(@comment.user), notice: @comment.errors.full_messages.join
    end
  end

这是我的 RSpec

  context "User logged in" do 
    before :each do 
      @user = create(:user)
      sign_in @user
    end 

    let(:comment) { create(:comment, user: @user, author_id: @user.id) }
    let(:comment_child) { create(:comment_child, user: @user, author_id: @user.id, parent_id: comment.id) }

    describe "POST #create" do 
      context "with valid attributes" do 
        it "saves the new comment object" do
          expect{ post :create, comment: attributes_for(:comment), id: @user.id}.to change(Comment, :count).by(1)
        end

        it "redirect to :show view " do 
          post :create, comment: attributes_for(:comment), user: @user
          expect(response).to redirect_to user_path(comment.user)
        end
      end

      ...
    end
  end

我的评论型号

class Comment < ActiveRecord::Base
    belongs_to :user
    acts_as_tree order: 'created_at DESC'

    VALID_REGEX = /\A^[\w \.\-@:),.!?"']*$\Z/
    validates :body, presence: true, length: { in: 2..240}, format: { with: VALID_REGEX }
end

如何将user_id添加到该请求中?当我将控制器redirect_to user_path(@comment.user)中的代码更改为redirect_to user_path(current_user)时 - 测试通过。我可以在评论控制器中redirect_to用户吗?有可能做得对吗?谢谢你的时间。

1 个答案:

答案 0 :(得分:2)

基本上,错误是由@comment.user为零而导致的。

让我们通过清理规范来开始修复它:

context "User logged in" do 

  # declare lets first.
  let(:user) { create(:user) }
  let(:comment) { create(:comment, user: user, author: user) }
  # use do instead of braces when it does not fit on one line.
  let(:comment_child) do 
    # use `user: user` instead of `user_id: user.id`.
    # the latter defeats the whole purpose of the abstraction.
    create(:comment_child, user: user, author: user, parent: comment)
  end

  before { sign_in(user) }

  describe "POST #create" do 
    context "with valid attributes" do 
      it "saves the new comment object" do
        expect do 
          post :create, comment: attributes_for(:comment) 
        end.to change(Comment, :count).by(1)
      end

      it "redirects to the user" do 
        post :create, comment: attributes_for(:comment)
        expect(response).to redirect_to user
      end
    end
  end
end

您通常应避免使用实例变量,而在大多数情况下使用lets。使用混合只会增加混乱,因为很难看到什么是延迟加载甚至实例化的地方。

然后我们可以处理实施:

def create
  @comment = current_user.comments.new(comment_params)

  if @comment.save
    redirect_to @comment.user, notice: 'Your comment was successfully added!'
  else
    # ...
  end
end

private 
  def comment_params
    # note that we don't permit the user_id to be mass assigned
    params.require(:comment).permit(:foo, :bar, :parent_id)
  end

基本上你可以减少很多过度复杂:

  • 如果没有经过身份验证的用户,则引发错误。使用Devise,您可以before_action :authenticate_user!
  • 从会话中获取用户 - 而不是参数。您不会希望或需要用户代表他人发表评论。
  • comments键中包裹参数。
  • 使用redirect_to @some_model_instance并让rails执行其多态路由魔术。
  • 如果用户尝试传递错误的parent_id,请让ActiveRecord抛出错误。

您的评论模型是否真的需要userauthor关系?当然其中一个就足够了。