Search code examples
ruby-on-railsrspecrspec-rails

RSpec: how do I correctly simulate #destroy failure


I'm having a very hard time simulating the failure of a destroy method in my controller.

My controller's destroy looks like this:

def destroy
   project = Project.find(params[:id])
   project.destroy

  if project.destroyed?
    render json: {
      project: nil,
      message: "The project was successfully deleted"
    }
  else
    render json: {
      message: "Could not delete project",
    }, status: :unprocessable_entity
  end
end

I'm trying to render the json in the else block in my test, but can't get it done. So far the particular test looks like this:

describe "DELETE #destroy" do

 let!(:project) { create(:project, :open) }

  context "when invalid" do

   it "returns an error if the project was not deleted" do
     expect(Project).to receive(:find).with(project.id.to_s).and_call_original
     expect(project).to receive(:destroy).and_return(false)
     delete :destroy, id: project
   end

 end
end

The test either returns the 'happy path' or gives me errors. At the moment:

 Failure/Error: expect(project).to receive(:destroy).and_return(false)

       (#<Project:0x007f87cf5d46a8>).destroy(*(any args))
           expected: 1 time with any arguments
           received: 0 times with any arguments

If anyone could point me in the right direction, and explain how I can simulate a 422, I'd be very grateful!


Solution

  • You want allow at the start, not expect. Expect is the assertion part.

    it "returns an error if the project was not deleted" do
      allow(Project).to receive(:find).with(project.id.to_s).and_call_original
      allow(project).to receive(:destroy).and_return(false)
    
      delete :destroy, id: project
    
      expect(Project).to have_received(:find)
      expect(project).to have_received(:destroy)
    end
    

    I'm assuming you're right with

    allow(Project).to receive(:find).with(project.id.to_s).and_call_original
    

    but I've never seen that before, I would normally do

    allow(Project).to receive(:find).with(project.id.to_s).and_return(project)