带有RSpec的测试更新请求失败

问题描述 投票:0回答:1

[当我尝试使用RSpec测试更新操作时遇到两个问题,这是controller文件:

#volunteers_controller.rb

module Api
  module V1
    class VolunteersController < ApplicationController
      before_action :find_volunteer, only: %i[show update destroy]

      def update
        @volunteer.update!(volunteer_params)
        head :no_content
      end

      private

      def find_volunteer
        @volunteer = Volunteer.find_by!(id: params[:id])
      end

      def volunteer_params
        params.require(:volunteer).permit(:image_url, :name, :job_desc)
      end

    end
  end
end

这里是test文件:

require 'rails_helper'

RSpec.describe Api::V1::VolunteersController, type: :request do
   ...

    describe '#update' do
       let(:volunteer) { Volunteer.create!( :image_url=>"first.jpg", :name=>"test1", :job_desc=>"description") }       
       let(:params){
            {:volunteer =>  {
                "image_url"=>"new.jpg", 
                "name"=>"test1", 
                "job_desc"=>"description"
            }
            }
        }

        it 'updates a certain volunteer' do
            patch :patch, :params => params #failed, bad URL
            expect(volunteer.image_url).to eq("new.jpg") #failed, still return 'first.jpg'
        end



        it 'returns a no_content header' do
            patch "http://localhost:3000/api/v1/volunteers/#{volunteer.id}", :params => params
            expect(response).to have_http_status "204"
        end

    end

end

private

   def json_parse(string)
     if string.class==String
       json = JSON.parse(string)
     end
       json
    end

所以我的问题是:

  1. [当尝试这样写URL:patch :patch, :params => params时,出现以下错误:
 Api::V1::VolunteersController#update updates a certain volunteer
     Failure/Error: patch :patch, :params => params

     URI::InvalidURIError:
       bad URI(is not URI?): "http://www.example.com:80patch"

如何将URL更改为:"http://localhost:3000/api/v1/volunteers/#{volunteer.id}"

  1. 我手动测试更新操作,将binding.pry放在update操作中,它确实更新了volunteer主题,但是,当返回测试时,它表明它没有被更新,这是为什么?

谢谢!

ruby-on-rails ruby unit-testing testing rspec
1个回答
1
投票

第一个问题实际上是您的更新方法本身,并且完全没有错误处理和对客户端的有意义的反馈。如果输入无效,则update!将提高ActiveRecord::RecordInvalid-这在控制器中根本无法挽救。而且异常不应用于正常的代码流-无效的输入并不是真正的例外事件。

相反,您应该重写控制器,以便它检查是否执行了更新并返回适当的响应:

def update
  if @volunteer.update(volunteer_params)
    head :no_content
  else
    head :unprocessable_entity
  end
end

至于规格本身,您正在混淆控制器规格和要求规格。尽管它们看上去有些相似,但主要区别在于,请求规范将实际的HTTP请求发送到您的rails服务器,而控制器规范对实际请求进行存根并将其传递给被测控制器的实例。

在控制器规格中,您可以编写:

patch :update, params: { ... }

因为它实际上在控制器的实例上调用update方法。但是,当然:

patch :patch, :params => params #failed, bad URL

在请求规范中将不起作用,因为它不是有效的URL,并且请求规范会发送实际的HTTP请求。请注意,您应该传递相对URL而不是绝对URL,因为测试服务器可能会运行在与开发服务器不同的端口上]

# Bad
patch "http://localhost:3000/api/v1/volunteers/#{volunteer.id}", :params => params

# Good
patch "/api/v1/volunteers/#{volunteer.id}", params: params 

ActiveRecord模型不是“实时重载”-更新数据库中的值时,不会自动更新内存中的表示。您需要手动重新加载记录才能实现:

it 'updates a certain volunteer' do
  patch "/api/v1/volunteers/#{volunteer.id}", params: params
  volunteer.reload
  expect(volunteer.image_url).to eq("new.jpg")
end

总的来说,您的规格实际上应该类似于:

# Describe the endpoint - not the controller implmentation
RSpec.describe "V1 Volunteers API", type: :request do
  describe 'PATCH /api/v1/volunteers/:id' do
    # use do ... end if the expression does not fit on one line
    let(:volunteer) do 
      # enough with the hashrockets already! 
      Volunteer.create!(
        image_url: "first.jpg", 
        name: "test1", 
        job_desc: "description"
      ) 
    end

    context "with invalid parameters" do
      # some set of failing parameters
      let(:params) do
        {
          volunteer:  {
            name: ""
          }
        }
      end

      it "returns unproccessable entity" do
        patch "/api/v1/volunteers/#{volunteer.id}", params: params 
        expect(resonse).to have_http_status :unproccessable_entity
      end

      it "does not update the volunteer" do
        patch "/api/v1/volunteers/#{volunteer.id}", params: params 
        expect { volunteer.reload }.to_not change(volunteer, :name).to("") 
      end
    end

    context "with valid parameters" do
      # some set of failing parameters
      let(:params) do
        {
          volunteer:  {
            image_url: "new.jpg", 
            name: "test1", 
            job_desc: "description"
          }
        }
      end

      it "returns no content" do
        patch "/api/v1/volunteers/#{volunteer.id}", params: params 
        expect(resonse).to have_http_status :no_content
      end

      it "updates the volunteer" do
        patch "/api/v1/volunteers/#{volunteer.id}", params: params 
        expect { volunteer.reload }.to change(volunteer, :image_url)
        .to("new.jpg") 
      end
    end
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.