【问题标题】:Rails 5 with Devise, testing Controllers with Rspec (destroy action)使用 Devise 的 Rails 5,使用 Rspec 测试控制器(销毁操作)
【发布时间】:2019-09-29 20:28:30
【问题描述】:

我正在为destroy操作执行rspec测试,其概念是登录用户只能销毁自己的帖子,而不能销毁其他用户创建的帖子。

The `new_post` is created by a user named `creator`, and another user named `user1` signed in and try to delete the `new_post`, it should not be able to delete it, because of the `  before_action :authenticate_user!, only: %i[create destroy]` in Posts controller

帖子控制器。

class PostsController < ApplicationController
  before_action :set_post, only: %i[show edit update destroy]
  before_action :current_user, only: %i[create destroy]
  before_action :authenticate_user!, only: %i[create destroy]
  .
  .
  .

  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  
  def post_params
    params.require(:post).permit(:content, :picture)
  end
end

用户控制器规范

require 'rails_helper'
RSpec.describe PostsController, type: :controller do
  context  'DELETE #destroy' do

    let(:user1) {User.create!(name:"John", email:"john@mail.com", password:"password")}

    let(:creator) { User.create!(name: "creator", email: "creaor@gmail.com", password: "password") }

    let(:new_post){creator.posts.create!(content: "Neque porro quisquam est qui dolorem ipsum")}

    
    it 'A user cannot delete a post created by other user' do
      sign_in user1
      p (new_post)
      expect { delete :destroy, params: { id: new_post.id } }.to change(Post, :count).by(0)
      
    end
  end
end

失败:

  1) PostsController DELETE #destroy A user cannot delete a post created by other user
     Failure/Error: expect { delete :destroy, params: { id: new_post.id } }.to change(Post, :count).by(0)
       expected `Post.count` to have changed by 0, but was changed by -1

【问题讨论】:

    标签: ruby ruby-on-rails-5 factory-bot rspec-rails


    【解决方案1】:

    我相信您需要在您的代码中添加一个授权检查。 authenticate_user! 验证发出请求的人已登录。但是,它不会检查用户是否授权发出他们正在发出的请求。 p>

    有关这两个概念的更多讨论,请参阅Authentication versus Authorization。并查看https://stackoverflow.com/a/25654844/868533,以全面了解 Rails 中流行的授权宝石。需要明确的是,您几乎肯定想要一种方法来验证用户 (Devise) 以及授权 gem。

    假设您决定使用 CanCanCan(这是我过去使用过的常用选项),您将添加一个 Ability 类,例如:

    class Ability
      include CanCan::Ability
    
      def initialize(user)
        if user.present?
          can :destroy, Post, user_id: user.id
        end
      end
    end
    

    然后您可以在控制器上添加before_action :check_authorization, only: %i[destroy] 作为新的before_action,您的测试应该无需任何修改即可通过。

    【讨论】:

    • cancancan 或 pundit 是一些值得注意的选项。
    • 谢谢你的澄清,这才是我真正需要的,它是授权而不仅仅是身份验证,所以我只是创建了一个简单的#authorized? 方法并且测试通过了def authorized? redirect to root_path unless @post.user_id == current_user.id end
    【解决方案2】:

    记住。您正在编写控制器测试。所以这个测试是unit test。在Devise 中有两种主要的授权方式。它们是授权路由和授权控制器。如果您使用授权路由,当您为控制器编写 rspec 时,您必须使用stub 来伪造授权访问。

    【讨论】:

    • 谢谢你的澄清,这才是我真正需要的,它是授权而不仅仅是身份验证,所以我只是创建了一个简单的#authorized?方法和测试通过 def 授权?重定向到 root_path 除非 @post.user_id == current_user.id 结束
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多