[英]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:所以我的问题是:
patch:patch, :params => params
, I got the following error: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}"
?
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.