title: RSpec test destroy method (Rails Tutorial 3.2 Ch. 9, Ex. 10)
body: <p>Note:  I've read <a href="https://stackoverflow.com/questions/10907325/rspec-test-for-destroy-if-no-delete-link">this</a> question and the answer, but for some reason the code isn't working for me. (see below for the error I'm getting)</p>
  
  <p>Exercise 10 from Chapter 9 of the Rails Tutorial asks you to:  Modify the destroy action [for users] to prevent admin users from destroying themselves. (Write a test first.)</p>
  
  <p>The tricky part here is testing it, because the application already hides the "delete" link for the current user, so you have to do the http request directly.  </p>
  
  <p>I got the code working, and tested it by removing the piece of code that hides the delete link for the current user.  Sure enough, if I click on the delete link for the currently logged-in user, it redirects me and gives me the notice message.  </p>
  
  <p><strong>From users_controller.rb</strong></p>
  
  <pre><code>  def destroy
      @user = User.find(params[:id])
      if current_user?(@user)
        redirect_to users_path, notice: "You can't destroy yourself."
      else
        @user.destroy
        flash[:success] = "User destroyed."
        redirect_to users_path
      end
    end
  </code></pre>
  
  <p>The problem I'm having is in writing the tests for this that will send the delete request and call the destroy method.  I tried the solution from <a href="https://stackoverflow.com/questions/10907325/rspec-test-for-destroy-if-no-delete-link">Rspec test for destroy if no delete link</a>, which I'm copying here:</p>
  
  <p><strong>From user_pages_spec.rb</strong></p>
  
  <pre><code> describe "destroy" do
      let(:admin) { FactoryGirl.create(:admin) }
  
      it "should not allow the admin to delete herself" do
        sign_in admin
        #expect { delete user_path(admin), method: :delete }.should change(User, :count)
        expect { delete :destroy, :id =&gt; admin.id }.should_not change(User, :count)
      end
    end
  </code></pre>
  
  <p>But when I run this, I get this error from RSpec</p>
  
  <pre><code>Failures:
  
    1) User Pages destroy should not allow the admin to delete herself
       Failure/Error: expect { delete :destroy, :id =&gt; admin.id }.should_not change(User, :count)
       ArgumentError:
         bad argument (expected URI object or URI string)
       # ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in &lt;top (required)&gt;'
       # ./spec/requests/user_pages_spec.rb:180:in `block (3 levels) in &lt;top (required)&gt;'
  </code></pre>
  
  <p>So, my questions are:
  1)  Why is this code above failing?
  2)  How do I simulate a "delete" in order to call the destroy action in my controller?   </p>
  
  <p>Environment:
  Mac OSX
  ruby 1.9.3p194
  Rails 3.2.3</p>
  
  <p>Gems for testing:<br>
  group :test do
      gem 'rspec-rails', '2.9.0'
      gem 'capybara', '1.1.2'
      gem 'rb-fsevent', '0.4.3.1', :require => false
      gem 'growl', '1.0.3'
      gem 'guard-spork', '0.3.2'
      gem 'spork', '0.9.0'
      gem 'factory_girl_rails', '1.4.0'
  end</p>
  
  <p><strong>More Info</strong>
  I have tried a <strong>ton</strong> of ways to try to simulate clicking on the delete link and none seem to work.  I've been using the debugger gem to see if the destroy method is even being called.  In the test that clicks on the link to delete a different user, the destroy method gets called and it works fine:</p>
  
  <pre><code>it "should be able to delete another user" do
    expect { click_link('delete') }.to change(User, :count).by(-1)
  end
  </code></pre>
  
  <p>But nothing I have tried to generate the delete request directly has worked to call the destroy method.  </p>
  
  <p>Thanks for your help!  </p>
  
  <p>Will</p>
  
  <p>** UPDATE **</p>
  
  <p>I tried DVG's suggestion:</p>
  
  <pre><code>describe "destroy" do
      let(:admin) { FactoryGirl.create(:admin) }
  
      it "should not allow the admin to delete herself" do
        sign_in admin
        #expect { delete user_path(admin), method: :delete }.should change(User, :count)
        expect { delete :destroy, :id =&gt; admin }.to_not change(User, :count)
      end
    end
  </code></pre>
  
  <p>And got this error:</p>
  
  <pre><code>6) User Pages destroy should not allow the admin to delete herself
       Failure/Error: expect { delete :destroy, :id =&gt; admin }.to_not change(User, :count)
       ArgumentError:
         bad argument (expected URI object or URI string)
       # ./spec/requests/user_pages_spec.rb:190:in `block (4 levels) in &lt;top (required)&gt;'
       # ./spec/requests/user_pages_spec.rb:190:in `block (3 levels) in &lt;top (required)&gt;'
  </code></pre>
  
  <p><strong>SOLUTION</strong></p>
  
  <p>I figured it out after FOREVER.  </p>
  
  <p>I had to use Rack::Test to issue the DELETE request, but Capybara and Rack::Test don't share the same MockSession, so I had to pull in the :remember_token and :!sample_app_session cookies and put them into the DELETE request manually.  Here is what worked.  (the other problem I was having, listed below, was that I had a force_ssl statement that was not letting my destroy action get called.  </p>
  
  <pre><code>describe "destroy" do
      let!(:admin) { FactoryGirl.create(:admin) }
  
      before do
        sign_in admin
      end
  
      it "should delete a normal user" do
        user = FactoryGirl.create(:user)
        expect { delete user_path(user), {},
         'HTTP_COOKIE' =&gt; "remember_token=#{admin.remember_token},
          #{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }.
          to change(User, :count).by(-1)
      end
  
      it "should not allow the admin to delete herself" do
        expect { delete user_path(admin), {},
         'HTTP_COOKIE' =&gt; "remember_token=#{admin.remember_token},
          #{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }.
         to_not change(User, :count)
      end
    end
  </code></pre>
  
  <p>I had a force_ssl statement after my before_filters in my users_controller.rb and this was somehow throwing things off so I never got to the destroy action.  </p>
  
  <pre><code>class UsersController &lt; ApplicationController
    before_filter :signed_in_user,  only: [:edit, :update, :index]
    before_filter :existing_user,   only: [:new, :create]
    before_filter :correct_user,    only: [:edit, :update]
    before_filter :admin_user,      only: :destroy
  
    #force_ssl
  
    def index
      @users = User.paginate(page: params[:page])
    end
  
    def show 
      @user = User.find(params[:id])
      @microposts = @user.microposts.paginate(page: params[:page])
    end
  
    def destroy
      @user = User.find(params[:id])
      if current_user?(@user)
        redirect_to users_path, notice: "You can't destroy yourself."
      else
        @user.destroy
        flash[:success] = "User destroyed."
        redirect_to users_path
      end
    end
  </code></pre>
  
  <p>These were helpful in getting to a solution</p>
  
  <p><a href="https://gist.github.com/484787" rel="nofollow noreferrer">https://gist.github.com/484787</a></p>
  
  <p><a href="http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/" rel="nofollow noreferrer">http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/</a></p>