简体   繁体   English

在Controller测试中测试销毁方法失败

[英]Testing destroy method failure in Controller test

I have a controller which destroys an item in my db. 我有一个控制器,它破坏了我数据库中的一个项目。 Currently it looks like this: 当前看起来像这样:

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

Now, I've written a few rspec controller tests to verify the happy path, but I'd like like to test the failure path (ie when @item.destroy fails). 现在,我编写了一些rspec控制器测试来验证满意的路径,但是我想测试失败的路径(即@ item.destroy失败时)。 I would imagine the correct way to do this is using some kind of mocking or stubbing, but I can't come up with something that works. 我可以想象,执行此操作的正确方法是使用某种模拟或存根,但我无法提出可行的方法。

I've tried the following with some variations, but it's not working: 我尝试了以下一些变化,但无法正常工作:

        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

If anyone out there can provide me some direction or sample code on how to do this, it would be appreciated. 如果有人可以提供有关该操作方法的指导或示例代码,我们将不胜感激。

Thanks 谢谢

How are you setting @item in the spec? 您如何在规范中设置@item I suspect it's not actually being stubbed. 我怀疑它实际上并没有被打断。

Update: 更新:

Without seeing your controller, I can't give the exact code, but normally it would be something like this: 在没有看到您的控制器的情况下,我无法提供确切的代码,但是通常情况是这样的:

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

Update 2: 更新2:

Expanding out, you set item with: 展开后,您可以通过以下方式设置item

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

This is a very long chain of calls. 这是一个很长的通话周期。 RSpec has ways of dealing this, but they are in a section called Working with legacy code which says Usage of these features should be considered a code smell . RSpec有解决此问题的方法,但是在“ 使用遗留代码”一节中它们说应将这些功能的使用视为一种代码味道

One approach to improve the code could be to introduce a 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

This is much simpler to stub, and helps to decouple the controller from the DB structure. 这对于存根而言更加简单,并且有助于将控制器与数据库结构分离。 destroy can now be stubbed with: 现在可以使用以下命令destroy

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM