提问人:Marcos Eduardo Santos Henke 提问时间:5/31/2023 更新时间:5/31/2023 访问量:67
使用 Rspec 测试在 Rails 中更新帖子时,如何修复 HTTP 状态代码 302?
How can I fix the HTTP status code 302 when updating a post in Rails using Rspec tests?
问:
我已经被这个问题困了几天了,请帮忙!
我的问题是:我正在尝试在帖子控制器的 Rails 中进行 Rspec 测试,但是当我进行更新时,发生了此错误,我不知道为什么
#posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: %i[show update destroy]
before_action :authenticate_user!, except: %i[index show]
def index
posts = Posts::List.new(params).execute
render json: posts, meta: pagination(posts), each_serializer: PostSerializer, status: :ok
end
def show
authorize @post
render json: @post, serializer: PostSerializer, list_comments: true, status: :ok
end
def create
@post = authorize Posts::Create.new(post_params, current_user).execute
render json: @post, serializer: PostSerializer, status: :created
end
def update
@post = authorize Posts::Update.new(post_params, @post).execute
render json: @post, serializer: PostSerializer, status: :ok
end
def destroy
authorize Posts::Destroy.new(@post).execute
head :ok
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = Post.find(params[:id])
authorize @post
end
# Only allow a list of trusted parameters through.
def post_params
params.require(:post).permit(:title, :description, :category_id)
end
end
我在操作中使用了一个资源,每个资源都有自己的资源,这个是更新操作资源
#resource update.rb
class Posts::Update
attr_accessor :params, :post
def initialize(params, post)
@params = params
@post = post
end
def execute
post.update!(mount_params)
end
private
def mount_params
{
title: params[:title] || post.title,
description: params[:description] || post.description,
category_id: params[:category_id] || post.category_id
}
end
end
有趣的是,创建操作在测试时有效,对我来说,与更新操作相比,它的操作非常相似
#posts_controller_spec.rb
require 'rails_helper'
require './spec/helpers/authentication_helper'
RSpec.describe PostsController, :focus, type: :controller do
include AuthenticationHelper
include Devise::Test::ControllerHelpers
attr_accessor :post_one, :post_two, :post_three, :user
before(:all) do
posts = FactoryBot.create_list(:post, 3, :with_comments)
@user = FactoryBot.create(:user)
@post_one = posts.first
@post_two = posts.second
@post_three = posts.third
end
let(:valid_headers) do
user.create_new_auth_token
end
let(:root_keys) { %w[post] }
let(:expected_post_keys) { %w[id title description category_id user_id] }
let(:expected_meta_keys) { %w[current_page per_page total_pages total_count] }
let(:error_root_keys) { %w[error] }
let(:expected_error_keys) { %w[message] }
let(:params) do
{
post: {
title: Faker::Quote.yoda,
description: Faker::Lorem.characters(number: 15),
category_id: FactoryBot.create(:category).id
}
}
end
describe 'GET #index' do
let(:root_keys) { %w[posts meta] }
before do
get :index
@body = JSON.parse(response.body)
end
it 'return status code :ok' do
expect(response).to have_http_status(:ok)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*root_keys)
end
it 'match with posts keys' do
@body['posts'].map do |post|
expect(post.keys).to contain_exactly(*expected_post_keys)
end
end
it 'match with meta keys' do
expect(@body['meta'].keys).to contain_exactly(*expected_meta_keys)
end
end
describe 'GET #show' do
context 'when post does not exists' do
let(:root_keys) { %w[error] }
let(:expected_error_keys) { %w[message] }
before do
get :show, params: { id: Faker::Number.number }
@body = JSON.parse(response.body)
end
it 'return status code :not_found' do
expect(response).to have_http_status(:not_found)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*error_root_keys)
end
it 'match with post keys' do
expect(@body['error'].keys).to contain_exactly(*expected_error_keys)
end
end
context 'when post exists' do
let(:expected_post_keys) { %w[id title description category_id user_id comments] }
let(:expected_comment_keys) { %w[id comment post_id created_at] }
before do
get :show, params: { id: post_one.id }
@body = JSON.parse(response.body)
end
it 'return status code :ok' do
expect(response).to have_http_status(:ok)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*root_keys)
end
it 'match with post keys' do
expect(@body['post'].keys).to contain_exactly(*expected_post_keys)
end
it 'match with comment keys' do
@body['post']['comments'].map do |comment|
expect(comment.keys).to contain_exactly(*expected_comment_keys)
end
end
end
end
describe 'POST #create' do
before do # criar um contexto onde o usuario tenta criar um post sem fazer o login
set_authentication_headers_for(user)
post :create, params: params
@body = JSON.parse(response.body)
end
it 'return status code :created' do
expect(response).to have_http_status(:created)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*root_keys)
end
it 'match with post keys' do
expect(@body['post'].keys).to contain_exactly(*expected_post_keys)
end
end
describe 'PUT #update' do
context 'when post does not exists' do
before do
set_authentication_headers_for(user)
put :update, params: params.merge({ id: Faker::Number.number })
@body = JSON.parse(response.body)
end
it 'return status code :not_found' do
expect(response).to have_http_status(:not_found)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*error_root_keys)
end
it 'match with post keys' do
expect(@body['error'].keys).to contain_exactly(*expected_error_keys)
end
end
context 'when post exists' do
before do
set_authentication_headers_for(user)
put :update, params: params.merge({ id: post_one.id })
@body = JSON.parse([response.body].to_json).first
end
it 'return status code :ok' do
expect(response).to have_http_status(:ok)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*root_keys)
end
it 'match with post keys' do
expect(@body['post'].keys).to contain_exactly(*expected_post_keys)
end
end
end
describe 'DESTROY #destroy' do
context 'when post does not exists' do
before do
set_authentication_headers_for(user)
delete :destroy, params: params.merge({ id: Faker::Number.number })
@body = JSON.parse(response.body)
end
it 'return status code :not_found' do
expect(response).to have_http_status(:not_found)
end
it 'match with root keys' do
expect(@body.keys).to contain_exactly(*error_root_keys)
end
it 'match with post keys' do
expect(@body['error'].keys).to contain_exactly(*expected_error_keys)
end
end
context 'when post exists' do
before do
set_authentication_headers_for(user)
delete :destroy, params: { id: post_one.id }
end
it 'return status code :no_content' do
expect(response).to have_http_status(:no_content)
end
end
end
end
PostsController
GET #index
return status code :ok
match with root keys
match with posts keys
match with meta keys
GET #show
when post does not exists
return status code :not_found
match with root keys
match with post keys
when post exists
return status code :ok
match with root keys
match with post keys
match with comment keys
POST #create
return status code :created
match with root keys
match with post keys
PUT #update
when post does not exists
return status code :not_found
match with root keys
match with post keys
when post exists
return status code :ok (FAILED - 1)
match with root keys (FAILED - 2)
match with post keys (FAILED - 3)
DESTROY #destroy
when post does not exists
return status code :not_found
match with root keys
match with post keys
when post exists
return status code :no_content (FAILED - 4)
Failures:
1) PostsController PUT #update when post exists return status code :ok
Failure/Error: expect(response).to have_http_status(:ok)
expected the response to have status code :ok (200) but it was :found (302)
# ./spec/controllers/posts_controllers_spec.rb:163:in `block (4 levels) in <top (required)>'
# ./spec/support/database_cleaner.rb:9:in `block (3 levels) in <main>'
# ./spec/support/database_cleaner.rb:8:in `block (2 levels) in <main>'
2) PostsController PUT #update when post exists match with root keys
Failure/Error: expect(@body.keys).to contain_exactly(*root_keys)
NoMethodError:
undefined method `keys' for #<String:0x0000555e1e849aa0>
# ./spec/controllers/posts_controllers_spec.rb:167:in `block (4 levels) in <top (required)>'
# ./spec/support/database_cleaner.rb:9:in `block (3 levels) in <main>'
# ./spec/support/database_cleaner.rb:8:in `block (2 levels) in <main>'
3) PostsController PUT #update when post exists match with post keys
Failure/Error: expect(@body['post'].keys).to contain_exactly(*expected_post_keys)
NoMethodError:
undefined method `keys' for nil:NilClass
# ./spec/controllers/posts_controllers_spec.rb:171:in `block (4 levels) in <top (required)>'
# ./spec/support/database_cleaner.rb:9:in `block (3 levels) in <main>'
# ./spec/support/database_cleaner.rb:8:in `block (2 levels) in <main>'
4) PostsController DESTROY #destroy when post exists return status code :no_content
Failure/Error: expect(response).to have_http_status(:no_content)
expected the response to have status code :no_content (204) but it was :found (302)
# ./spec/controllers/posts_controllers_spec.rb:204:in `block (4 levels) in <top (required)>'
# ./spec/support/database_cleaner.rb:9:in `block (3 levels) in <main>'
# ./spec/support/database_cleaner.rb:8:in `block (2 levels) in <main>'
Finished in 1.64 seconds (files took 5.32 seconds to load)
24 examples, 4 failures
Failed examples:
rspec ./spec/controllers/posts_controllers_spec.rb:162 # PostsController PUT #update when post exists return status code :ok
rspec ./spec/controllers/posts_controllers_spec.rb:166 # PostsController PUT #update when post exists match with root keys
rspec ./spec/controllers/posts_controllers_spec.rb:170 # PostsController PUT #update when post exists match with post keys
rspec ./spec/controllers/posts_controllers_spec.rb:203 # PostsController DESTROY #destroy when post exists return status code :no_content
我搜索了类似的错误,但找不到解决我问题的错误
答:
0赞
Fer
5/31/2023
#1
您正在收到重定向(可能重定向到登录页面)
我的猜测是用户无法编辑帖子,因为它不属于他。
我会改变您创建帖子的方式,使其属于用户。
所以改变
posts = FactoryBot.create_list(:post, 3, :with_comments)
@user = FactoryBot.create(:user)
到
@user = FactoryBot.create(:user)
posts = FactoryBot.create_list(:post, 3, :with_comments, user_id: @user.id)
可能会解决您的问题。
奖励:我还会针对您现在遇到的情况添加测试:如果用户无权更改帖子(无论是更新还是删除),则应将其重定向,最重要的是,帖子不会更改(或不会删除,具体取决于正在测试的操作)
评论
0赞
Marcos Eduardo Santos Henke
6/1/2023
非常感谢,你是对的,多亏了你的帮助,我设法克服了这个问题,但现在又出现了一个(总是像这样哈哈),出现的错误与我用于授权的权威宝石有关,这是出现的错误:失败/错误:@post = authorize Posts::Update.new(post_params, @post).execute Expert::NotDefinedError:无法找到策略,您也能帮我解决这个问题吗?TrueClassPolicy
true
0赞
Fer
6/1/2023
我不熟悉专家。但是对于我所读到的内容,您的返回的是 true,而不是 Post 对象。Posts::Update.new(post_params, @post).execute
0赞
Fer
6/1/2023
我不确定你是否正确使用它。我只是根据你的实现来猜测:如果不允许用户创建帖子,那么为时已晚,因为已经执行了。我还建议你阅读更多关于rails如何从参数中更新记录的信息(关键词:Rails Strong Parameters),因为你这样做的方式不是“rails的方式”Posts::Update
authorize
Posts::Create
0赞
Marcos Eduardo Santos Henke
6/2/2023
好的,我会调查你说的,非常感谢!
评论