ruby-on-rails 在使用Rspec测试更新Rails中的帖子时,如何修复HTTP状态码302?

ioekq8ef  于 2023-06-07  发布在  Ruby
关注(0)|答案(1)|浏览(139)

我已经在这个问题上困了好几天了,请帮帮忙!
我的问题是:我试图在Rails中的posts控制器中进行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

有趣的是,create操作在测试时有效,对我来说,它的操作与update操作非常相似

#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

我搜索了类似的错误,但我找不到一个可以解决我的问题

aydmsdu9

aydmsdu91#

您正在获得重定向(可能是到登录页面)
我的猜测是,用户无法编辑帖子,因为它不属于他。
我会改变你创建帖子的方式,使其属于用户。
如此多变

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)

也许能解决你的问题
额外的好处:我也会为你现在的情况添加测试:如果用户没有被授权更改一个帖子(无论是更新还是删除),那么它应该被重定向,最重要的是,帖子不会更改(或者不会被删除,这取决于正在测试的操作)

相关问题