在控制器中为删除操作编写rspec测试

时间:2016-07-25 08:36:42

标签: ruby-on-rails ruby rspec factory-bot

我的控制器中有一个删除功能,如下所示:

before_action :set_form, only: [:show, :edit, :update, :destroy]
 def destroy
    if @form.destroy
            render json: @form, status: :ok
        else
            render json: @form.errors, status: :not_found
        end
  end

 private

  def set_form
    @form = Form.find(params[:id])
  end

 end

我有两个问题:

1)我没有正确执行删除时返回404。这是合理的状态代码吗?我查看了所有4XX状态代码,这是最有意义的。

2)我不确定我将如何为render json: @form.errors, status: :not_found编写测试。

以下是我的尝试:

context "Cannot delete the model" do
    it "responds successfully with an HTTP 404 status code" do
        delete :destroy, id: 100000
        expect(response).to have_http_status(404)
    end
end

问题是我收到错误消息ActiveRecord::RecordNotFound:Couldn't find Visa with 'id'=10000而不是delete操作实际失败。如何模拟delete操作失败?

3 个答案:

答案 0 :(得分:1)

该错误与您在开发甚至生产中获得的错误相同。这条线...

@form = Form.find(params[:id])

如果未找到包含已发送ID的Form记录,则会引发异常。

如果找不到@form并且您找到了继续的机制,@form.destroy仍然无效,因为destroy个对象上没有方法nil,并且errors个对象上也没有方法nil

如果您坚持按照描述处理破坏方法,则需要执行类似......

的操作
before_action :set_form, only: [:show, :edit, :update] #remove destroy
def destroy
  @form = Form.find_by(id: params[:id])
  if @form
    @form.destroy
    render json: @form, status: :ok
  else
    render json: 'record not found for delete', status: :not_found
  end
end

答案 1 :(得分:0)

您的set_form方法会引发该错误,您必须将其捕获或使用find_by_id中的destroy

def set_form
  @form = Form.find(params[:id])
rescue ActiveRecord::RecordNotFound
end

这种方式@formnil if

对于HTTP状态代码,您可以考虑使用422

答案 2 :(得分:0)

当您在ID无效的记录上调用ActiveRecord::RecordNotFound时,Rails会引发.find。这是一个非常理想的行为,因为它为无法找到资源提供了一个很好的捕获。

默认设置是发送404 - 未找到的标头。对于HTML请求类型,它还会发送默认错误页面。

相反,问题在于你如何测试它。

如果要测试rails会引发错误:

context "Cannot delete the model" do
  it "raises an error when the id is not valid" do
    bypass_rescue
    expect { delete :destroy, id: 100000 }.to raise_error(ActiveRecord::RecordNotFound)
    expect(response).to have_http_status(404)
  end
end

但是,如果您想测试删除模型因任何其他原因(记录被锁定或用户未获得授权)失败,您应该在数据库中使用灯具或工厂设置记录并提供有效的ID - 您还应该提供正确的响应代码(422或401)。

至于模拟删除失败,您可以使用allow_any_instance_of(SomeModel).to receive(:destroy).and_return(false)