在控制器测试中测试销毁方法失败
Testing destroy method failure in Controller test
我有一个控制器可以破坏我数据库中的一个项目。目前看起来像这样:
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 有处理这个问题的方法,但它们在名为 Working with legacy code 的部分中,其中说明 这些功能的使用
应该算是代码味道.
改进代码的一种方法是引入 service object:
class FindItem
def self.call(item_id, collection_id, user)
user.collections.find(params[:collection_id]).items.find_by_id(item)
end
end
存根要简单得多,并且有助于将控制器与数据库结构分离。 destroy
现在可以存根:
item = double
allow(FindItem).to receive(:call).and_return(item)
allow(item).to receive(:destroy).and_return(false)
我有一个控制器可以破坏我数据库中的一个项目。目前看起来像这样:
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 有处理这个问题的方法,但它们在名为 Working with legacy code 的部分中,其中说明 这些功能的使用 应该算是代码味道.
改进代码的一种方法是引入 service object:
class FindItem
def self.call(item_id, collection_id, user)
user.collections.find(params[:collection_id]).items.find_by_id(item)
end
end
存根要简单得多,并且有助于将控制器与数据库结构分离。 destroy
现在可以存根:
item = double
allow(FindItem).to receive(:call).and_return(item)
allow(item).to receive(:destroy).and_return(false)