Problems testing a destroy failure

Hello,    I'm trying to test an active record object destroy failure but I'm having problems creating a failure situation. I have a before_filter method called 'require_user_payment_info' which validates the @payment_info object before the delete method is called so I can't create a 'bad' @payment_info object before the delete method is called.

Here's the require_user_payment_info method:

[code]   def require_user_payment_info     @payment_info = credit_card_model.slave.find_by_user_guid(user_guid)     if !@payment_info || @payment_info.user_guid != user_guid       redirect_to(:controller => 'store', :action => 'index') and return false     else       if((@payment_info.card_expires_year.to_i < Date.today.year) ||           ((@payment_info.card_expires_month.to_i < Date.today.month) && (@payment_info.card_expires_year.to_i == Date.today.year)))         @payment_info.card_account_public = "" #clear this out so the user is forced to re-enter the credit card number         @payment_info.valid?         flash.now[:error] = t('ui_flash_errors.card_expired_error')       end     end   end [/code]

And the actual delete method:

[code]   def delete     # required to be called via a delete request     redirect_to :action => 'edit' and return if !request.delete?     if @payment_info.destroy       flash[:notice] = "Delete SUCCESSFUL"       redirect_to :action => 'new'     else       flash[:error] = "Delete failed"       redirect_to :action => 'edit'     end [/code]

Any ideas?

Thanks!

Hello,   I'm trying to test an active record object destroy failure but I'm having problems creating a failure situation. I have a before_filter method called 'require_user_payment_info' which validates the @payment_info object before the delete method is called so I can't create a 'bad' @payment_info object before the delete method is called.

Here's the require_user_payment_info method:

[code] def require_user_payment_info    @payment_info = credit_card_model.slave.find_by_user_guid(user_guid)    if !@payment_info || @payment_info.user_guid != user_guid      redirect_to(:controller => 'store', :action => 'index') and return false    else      if((@payment_info.card_expires_year.to_i < Date.today.year) ||          ((@payment_info.card_expires_month.to_i < Date.today.month) && (@payment_info.card_expires_year.to_i == Date.today.year)))        @payment_info.card_account_public = "" #clear this out so the user is forced to re-enter the credit card number        @payment_info.valid?        flash.now[:error] = t('ui_flash_errors.card_expired_error')      end    end end [/code]

And the actual delete method:

[code] def delete

Shouldn't this line be 'def destroy'?

Delete is the method that is called without callbacks to actually remove the record. Destroy calls up the before_destroy handlers and checks the validations.

Here's one ripped from a working application:

#group.rb   before_destroy :group_has_children? ...   private   def group_has_children?     errors.add(:base, "Cannot delete a group with members") unless groupings.count == 0     errors.blank?   end

There's probably a better way to do this, but this was how I settled on it. I know there's a way to do this with validations, too. Probably just this:

  validate :group_has_children?, :on => :destroy

  def group_has_children?     errors.add(:base, "Cannot delete a group with members") unless groupings.count == 0   end

Walter

Walter - thanks for getting back to me on this.

First of all, I should have made this more clear - the delete action is in the controller, not the model and the @payment_info model object is using the destroy method.

To me, this method seems to be as straight forward as possible - you attempt to destroy a verified active record object and if the destroy is successful, then redirect to the the new action, if it fails display the error and redirect to the edit method. I don't see a logic problem anywhere in this method so I'm not a big fan of trying to change the method just to satisfy testing.

Walter - thanks for getting back to me on this.

First of all, I should have made this more clear - the delete action is in the controller, not the model and the @payment_info model object is using the destroy method.

To me, this method seems to be as straight forward as possible - you attempt to destroy a verified active record object and if the destroy is successful, then redirect to the the new action, if it fails display the error and redirect to the edit method. I don't see a logic problem anywhere in this method so I'm not a big fan of trying to change the method just to satisfy testing.

I'm hardly an expert here. I looked in a simple site I did a while back using scaffold, since I don't have my Rails 4 legs under me yet, and scaffold maps the DELETE REST method to YourController::destroy. If you're using the generic routing, then the DELETE request will go to destroy. Apologies if your routes are not the same as mine, and you've already ruled this out.

Walter

I should add that this is a rails 2.3.18 (*sigh*) app. I see what you're saying about this code not being as RESTfully optimal as it could be but I feel that point is diverging off the original question: "how do I get @payment_info.destroy to fail in a test?"

I should add that this is a rails 2.3.18 (sigh) app. I see what you’re saying about this code not being as RESTfully optimal as it could be but I feel that point is diverging off the original question: “how do I get @payment_info.destroy to fail in a test?”

I would usually stub it (using mocha, rspec etc) to return/raise as appropriate. Alternatively you could submit data that you know will fail validation

Fred

That was my original attempt too but the require_user_payment_info before_filter method checks for a valid payment_info object and will redirect away from the delete method if the @payment_info object isn't valid. If I mock the @payment_info object to be invalid, it won't make it to the delete method.

I must admit my mocking knowledge isn't very strong so my attempts at mocking have been failures.

Here's the before_filter configuration for require_user_payment_info:

before_filter :require_user_payment_info, :only => [:show, :edit, :update, :delete]

That was my original attempt too but the require_user_payment_info

before_filter method checks for a valid payment_info object and will

redirect away from the delete method if the @payment_info object isn't

valid. If I mock the @payment_info object to be invalid, it won't

make it to the delete method.

In which case you want to just stub out the destroy method. In rspec for example you could do

PaymentInfo.any_instance.should_receive(:destroy).and_return(false)

And mocha can do something similar.

Fred

Fred - big thanks on this. I'm going to give it a try shortly and get back to you.

Bingo - that totally worked and here's my solution:

[code] def test_unsuccessful_delete     payment_info = Factory.create(:payment_info, :user_guid=>@user.guid, :card_expires_month=>'04',                                     :card_expires_year=>(Date.today.year+2).to_s, :cardholder_city=>"test city",                                       :cardholder_state=>'NC', :cardholder_country=>'US', :cardholder_zip=>'27612')     PaymentInfo.any_instance.stubs(:destroy).returns(false)

    delete(:delete, {}, @session)     assert_response(:redirect)     assert_equal false, assigns(:payment_info).blank?     assert_redirected_to({:controller=>'account', :action=>'edit'})     assert_equal flash[:error], "There was an error deleting your credit card information. Please try again."   end [/code]

Thanks for your help!