简体   繁体   English

RSpec 的测试更新请求失败

[英]test update request with RSpec failed

I have two problems when I try to test the update action with RSpec, here is the controller file:当我尝试使用 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

Here is the test file:这是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

So my questions are:所以我的问题是:

  1. when try to write the URL like this: patch:patch, :params => params , I got the following error:当尝试像这样编写 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"

How can I change the URL to: "http://localhost:3000/api/v1/volunteers/#{volunteer.id}" ?如何将 URL 更改为: "http://localhost:3000/api/v1/volunteers/#{volunteer.id}"

  1. I manually test the update action, putting a binding.pry in the update action, it does update volunteer subject, however, when it goes back to the test, it shows that it doesn't not get updated, why is that?我手动测试更新操作,在update操作中放了一个binding.pry ,它确实更新了volunteer主题,但是,当它回到测试时,它显示它没有得到更新,这是为什么呢?

Thank you!!谢谢!!

The first problem is really your update method itself and its complete lack of error handling and meaningful feedback to the client.第一个问题实际上是您的更新方法本身以及它完全缺乏错误处理和对客户端的有意义的反馈。 update! will raise ActiveRecord::RecordInvalid if the input is invalid - which is not rescued at all in your controller.如果输入无效,将引发ActiveRecord::RecordInvalid - 在您的 controller 中根本没有挽救。 And exceptions should no be used for normal code flow - invalid input is not really an exceptional event.异常不应用于正常的代码流 - 无效输入并不是真正的异常事件。

Instead you should rewrite your controller so that it checks if the update is performed and returns the appropriate response:相反,您应该重写您的 controller 以便它检查是否执行了更新并返回适当的响应:

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

As for the spec itself you're mixing up controller specs and request specs.至于规范本身,您正在混淆 controller 规范和请求规范。 While they look somewhat similar the key difference is that a request spec sends actual HTTP requests your rails server while a controller spec stubs the actual request and passes it to an instance of the controller under test.虽然它们看起来有些相似,但主要区别在于请求规范发送实际的 HTTP 请求您的 Rails 服务器,而 controller 规范将实际请求存根并将其传递给正在测试的 Z594C103F2C6E04C01D8AB059F03 的实例。

In a controller spec you could write:在 controller 规范中,您可以编写:

patch :update, params: { ... }

Because its actually calling the update method on an instance of the controller.因为它实际上是在 controller 的实例上调用更新方法。 But of course:但是当然:

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

Will not work in request spec since its not a valid URL and request specs send actual HTTP requests.在请求规范中不起作用,因为它不是有效的 URL 并且请求规范发送实际的 HTTP 请求。 Note that you should pass relative URLs and not absolute URLs as the test server may run on a different port then the dev server请注意,您应该传递相对 URL 而不是绝对 URL,因为测试服务器可能在与开发服务器不同的端口上运行

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

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

ActiveRecord models are not "live reloading" - the representation in memory will not automatically be updated when the values in the database are updated. ActiveRecord 模型不是“实时重新加载” - 当数据库中的值更新时,memory 中的表示不会自动更新。 You need to manaully reload the record for that to happen:您需要手动重新加载记录才能发生这种情况:

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

Altogether your spec should actually look something like:总的来说,您的规范实际上应该类似于:

# 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM