ruby on rails -在Controller测试中测试destroy方法失败



我有一个控制器,它在我的数据库中销毁一个项目。目前看起来是这样的:

before_filter(:except => :toggle_item_owned_state) do
    @collection = current_user.collections.find(params[:collection_id])
end
def destroy
    @item = @collection.items.find_by_id(params[:id])
    if @item.destroy
        redirect_to collection_items_path(@collection)
    else
        flash.now[:alert] = "There was a problem deleting this item."
        redirect_to root_path
    end
end

现在,我已经写了一些rspec控制器测试来验证快乐路径,但我想测试失败路径(即当@item.destroy失败时)。我想正确的方法是使用某种嘲弄或存根,但我想不出有效的方法。

我已经尝试了以下一些变化,但它不工作:

        context "delete fails" do
            before(:each) do
                allow(@item).to receive(:destroy).and_return(false)
                delete :destroy, :collection_id => batman_collection.id, :id => item_in_collection.id
            end
            it "will generate a flash error message" do
                expect(flash[:alert]).to eq("There was a problem saving your collection.")
            end
        end

如果有人在那里可以为我提供一些指导或示例代码如何做到这一点,我将不胜感激。

谢谢

如何在规范中设置@item ?我怀疑它实际上并没有被存根。

更新:

没有看到你的控制器,我不能给出确切的代码,但通常是这样的:

item = double
allow(Item).to receive(:find).and_return(item)
allow(item).to receive(:destroy).and_return(false)
更新2:

展开后,用:

设置item
current_user.collections.find(params[:collection_id]).items.find_by_id(params[:id])

这是一个很长的调用链。RSpec有处理这个问题的方法,但它们在"处理遗留代码"一节中,其中说这些特性的使用.

改进代码的一种方法是引入一个服务对象:
class FindItem
  def self.call(item_id, collection_id, user)
    user.collections.find(params[:collection_id]).items.find_by_id(item)
  end
end

这更容易存根,并有助于将控制器与DB结构解耦。destroy现在可以存根:

item = double
allow(FindItem).to receive(:call).and_return(item)
allow(item).to receive(:destroy).and_return(false)

最新更新