Rails Rspec测试Post更新/删除时HTTP 302错误排查求助
Rails Rspec测试中Update和Destroy动作返回302而非预期状态码问题
在Rails项目中使用Rspec测试Posts控制器时,执行update动作返回HTTP 302状态码(而非预期的200),destroy动作也返回302(而非预期的204),但create动作测试正常。update动作使用独立的Posts::Update资源类,其逻辑与create类似。以下是相关代码及测试失败详情:
相关代码
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
Posts::Update资源类(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 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
已搜索类似错误但未找到合适的解决方案。
内容的提问来源于stack exchange,提问作者Marcos Eduardo Santos Henke
相关产品推荐
相关产品推荐

