# transaction.rbclassTransaction<ActiveRecord::Baseafter_create:transfer_creditsattr_accessible:creditsbelongs_to:send_account,:class_name=>"Account"belongs_to:recv_account,:class_name=>"Account"privatedeftransfer_creditsbeginActiveRecord::Base.transactiondosend_account.deposit!(-credits)recv_account.deposit!(credits)endrescue=>eRails.logger.infoe.messageendendend# transaction_spec.rbcontext"exception raised"dobeforedo@send_account=create:account,:credits=>1000@transaction=Transaction.new@transaction.send_account=@send_account@transaction.credits=1recv_account=double(:account).as_null_object@transaction.stub(:recv_account){recv_account}recv_account.should_receive(:deposit!).and_raise(StandardError.new("Transfer failed!"))endit"should not transfer the credits"do@transaction.save@send_account.reload@send_account.credits.should==1000endend
But the result is the spec fails and says
12345678910
Failures:
1) Transaction status exception raised should not transfer the credits
Failure/Error: @send_account.credits.should == 1000
expected: 1000
got: 999 (using==)# ./spec/models/transaction_spec.rb:185:in `block (4 levels) in <top (required)>'Finished in 1.73 seconds
1 example, 1 failure
It did deduct the credits from the send_account by 1 while i was expecting it should do nothing to it. What’s going wrong, the doc says it will rollback the transaction, then I tried to test if calling transfer_credits explicitly is going to work. And yes, it works. Here’s the revised version:
# transaction.rbclassTransaction<ActiveRecord::Baseattr_accessible:creditsbelongs_to:send_account,:class_name=>"Account"belongs_to:recv_account,:class_name=>"Account"deftransfer_creditsbeginActiveRecord::Base.transactiondosend_account.deposit!(-credits)recv_account.deposit!(credits)endrescue=>eRails.logger.infoe.messageendendend# transaction_spec.rbdescribe"#transfer_credits"dobeforedo@send_account=create:account,:credits=>1000@transaction=Transaction.new@transaction.send_account=@send_account@transaction.credits=1recv_account=double(:account).as_null_object@transaction.stub(:recv_account){recv_account}recv_account.should_receive(:deposit!).and_raise(StandardError.new("Transfer failed!"))endit"should not transfer the credits"do@transaction.transfer_credits@send_account.reload@send_account.credits.should==1000endend
GOTCHA: DO NOT place ATOMIC operations within callbacks, like before_*, after_*