【问题标题】:rspec - How to test ActiveRecord::RecordNotFound?rspec - 如何测试 ActiveRecord::RecordNotFound?
【发布时间】:2026-01-12 02:30:01
【问题描述】:

我有一个更新人物属性的方法,如果找不到人物,它会拯救ActiveRecord::RecordNotFound。方法是:

  def update
    @people= People.find(params[:id])
    if @people.update(people_params)
      render json: { success: 'Success' }
    else
      render :edit
    end
  rescue ActiveRecord::RecordNotFound => e
    render json: { error: 'Failed') }
  end

我想测试一下没有找到记录的情况,下面是我的测试:

    let(:people) { create(:people) }
    let(:people_id) { people.id }
    let(:user) { people}
    # Other tests...
    context 'when person not found' do
      let(:exception) { ActiveRecord::RecordNotFound }

      # What should I write so that I can let the record not been found?

      before { allow(People).to receive(:find).and_raise(exception) }

      it 'responds with json containing the error message' do
        expect(JSON.parse(response.body)).to eq({error:'Error'})
      end
    end

我希望在未找到记录的情况下执行我的测试。但我不知道该怎么做。我试图设置let(people) {nil},但它不起作用。有没有办法做到这一点?谢谢!

【问题讨论】:

  • 为什么您的测试不起作用?我觉得很好
  • 不是错字吗?你的班级是People,而不是Person
  • 你也错过了这个请求,或者你在一些bedore/let中做它?
  • 谢谢大家,我刚刚找到了解决办法,导致找不到记录的方法是把人的id改成un-exit的。
  • 由于 DRY 原则,通常您不需要在控制器中挽救 RecordNotFound 错误。您可以在父控制器中定义它

标签: ruby-on-rails ruby unit-testing rspec


【解决方案1】:

这不是一个好的解决方案。在 Rails 中,您希望使用 rescue_from 来处理控制器级别的常见错误。

class ApplicationController
  rescue_from ActiveRecord::RecordNotFound, with: :not_found

  def not_found
    respond_to do |format|
      format.json { head :404 }
    end
  end
end

这让您可以使用继承来干燥您的代码。

render json: { error: 'Failed') }

是一个巨大的反模式。如果请求失败,您应该通过发送正确的 HTTP 状态代码告诉客户端。不要重新发明*。尤其是当您的解决方案是方轮时。如果您的 JS 依赖于使用 json 响应来查看请求是否成功,那么您做错了。

如果您想测试您的控制器是否正确处理丢失的资源,您可以这样做:

let(:people) { create(:people) }
let(:people_id) { people.id }
let(:user) { people}

it "returns the correct response code if the person cannot be found" do
  get '/people/notarealid'
  expect(response).to have_http_status :not_found
end

这不使用任何存根并实际测试实现。

【讨论】:

    【解决方案2】:

    你可以试试:

    let!(:error_failed) { { error: 'Failed' } }
    
    context 'when people is not found by params' do
      it 'return 404 and render json failed'
        null_object = double.as_null_object
        allow(People).to receive(:find).with(params[:id]).and_raise(ActiveRecord::RecordNotFound.new(null_object)
    
        put :update, format: :json, .....
        expect(response.body).to error_dailed.to_json
        expect(response.status).to .....
      end
    end
    

    【讨论】: