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!