如何用RSpec测试PUT/PATCH API?更新记录未生效排查
Rails RSpec PUT/PATCH 测试中记录未更新的问题排查
我正在测试API的PUT/PATCH端点,但测试里的record_to_update没有按预期更新。用Postman测试同一个端点是正常的,使用Rails 5.2和RSpec 3.8编写的测试代码如下:
require 'rails_helper' RSpec.describe UsersController, type: :controller do context 'when request with no valid headers' do ... ... ... end context 'when resquest with valid headers' do before do request.accept = 'application/vnd.api+json' request.content_type = 'application/vnd.api+json' 2.times do create(:user) end end describe 'PATCH/PUT /users/:id' do let(:record_to_update) { create(:user) } let(:user_params) do { id: first_user.id, name: 'goku', email: 'goku@bol.com' } end before do put :update, params: { id: record_to_update, user: user_params } record_to_update.reload end it 'should update user' do expect(record_to_update.name).to eq('goku') expect(response.status).to eq 200 end end end end
问题原因分析
我帮你梳理下测试代码里的几个关键错误:
- 参数结构与目标ID错误:你在
user_params里误用了未定义的first_user.id,而且没必要在更新参数里传递id——Rails的update动作是通过URL参数里的id(也就是params[:id])来定位要修改的记录的,额外传递的id要么被控制器的strong params过滤,要么会干扰更新逻辑。 - 请求参数传递不规范:调用
put :update时,你直接传了record_to_update(User对象)作为id参数,虽然Rails可能会自动转成ID,但更关键的是你把包含错误ID的user_params传给了user参数,导致控制器可能在更新错误的记录,或者参数被过滤后没有实际更新字段。
修复后的测试代码
require 'rails_helper' RSpec.describe UsersController, type: :controller do context 'when request with no valid headers' do ... ... ... end context 'when request with valid headers' do before do request.accept = 'application/vnd.api+json' request.content_type = 'application/vnd.api+json' 2.times do create(:user) end end describe 'PATCH/PUT /users/:id' do let(:record_to_update) { create(:user) } # 只传递需要更新的字段,移除多余的id参数 let(:user_params) do { name: 'goku', email: 'goku@bol.com' } end before do # 明确传递目标记录的ID,参数结构符合控制器预期 put :update, params: { id: record_to_update.id, user: user_params } record_to_update.reload end it 'should update user' do expect(record_to_update.name).to eq('goku') expect(response.status).to eq 200 end end end end
额外检查点
除了测试代码,还要确认你的UsersController里的逻辑是否正确:
- 确保
update动作正确通过params[:id]查找记录:
def update @user = User.find(params[:id]) if @user.update(user_params) render json: @user, status: :ok else render json: @user.errors, status: :unprocessable_entity end end
- 确认strong params允许更新
name和email字段:
private def user_params params.require(:user).permit(:name, :email) end
如果控制器的strong params没有包含要更新的字段,即使请求参数正确,也不会修改记录,这也是Postman能成功但测试失败的潜在原因之一。
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

