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). 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? 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:
Expanding out, you set item
with:
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 .
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:
item = double
allow(FindItem).to receive(:call).and_return(item)
allow(item).to receive(:destroy).and_return(false)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.