Rails 5 使用 Devise,使用 Rspec 测试控制器(销毁操作)
Rails 5 with Devise, testing Controllers with Rspec (destroy action)
我正在为 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
我认为您需要在代码中添加 授权 检查。 authenticate_user!
验证 发出请求的人已登录。但是,它不会检查用户是否授权他们提出的要求。
有关 Rails 中流行授权 gem 的详细概述,请参阅 Authentication versus Authorization for a bit more discussion on the two concepts. And take a look at 。明确地说,您几乎肯定想要一种方法来验证用户 (Devise
) 以及授权 gem.
假设您决定使用 CanCanCan
(这是我过去使用过的常见选项),您将添加一个 Ability
class,例如:
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
添加到您的控制器上,您的测试应该会在没有任何修改的情况下通过。
记住。您正在编写控制器测试。所以这个测试是unit test
。 Devise
中主要有两种授权方式。它们是授权路由和授权控制器。如果你使用授权路由,当你为控制器写rspec时,你必须使用stub
来伪造一个授权访问。
我正在为 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
我认为您需要在代码中添加 授权 检查。 authenticate_user!
验证 发出请求的人已登录。但是,它不会检查用户是否授权他们提出的要求。
有关 Rails 中流行授权 gem 的详细概述,请参阅 Authentication versus Authorization for a bit more discussion on the two concepts. And take a look at 。明确地说,您几乎肯定想要一种方法来验证用户 (Devise
) 以及授权 gem.
假设您决定使用 CanCanCan
(这是我过去使用过的常见选项),您将添加一个 Ability
class,例如:
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
添加到您的控制器上,您的测试应该会在没有任何修改的情况下通过。
记住。您正在编写控制器测试。所以这个测试是unit test
。 Devise
中主要有两种授权方式。它们是授权路由和授权控制器。如果你使用授权路由,当你为控制器写rspec时,你必须使用stub
来伪造一个授权访问。