[当我尝试使用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
所以我的问题是:
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}"
?
binding.pry
放在update
操作中,它确实更新了volunteer
主题,但是,当返回测试时,它表明它没有被更新,这是为什么? 谢谢!
第一个问题实际上是您的更新方法本身,并且完全没有错误处理和对客户端的有意义的反馈。如果输入无效,则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