如何避免在rails中使用回调

时间:2012-01-05 12:09:50

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.1

Rails中的这个回调事情一直困扰着我。问题是,我不喜欢他们。主要是因为他们减慢了我的测试速度,因为我必须在我的单元测试中点击数据库以便持久化对象,这会触发回调(例如,after_save)。

我将使用一个简单的例子说明我想做些什么来让自己更清楚。假设我有一个账户,每次我提款,我都要从余额中扣除金额。我的模特是:

class Withdraw < ActiveRecord::Base
  belongs_to :account

  after_save :update_account_balance

  private
    def update_account_balance
      self.account.balance -= self.amount
      self.account.save
    end
end

class Account < ActiveRecord::Base
  has_many :withdraws
end

所以,如果我要测试那种行为,我将要做的是(使用RSpec):

describe Withdraw

  it 'updates the account balance' do
    account = Account.create({ :name => "foo", :balance => 100 })
    withdraw.create({ :amount => 10, :account => account })
    account.balance.should == 90
  end
end

请注意,我必须在该单元测试中两次点击数据库。这在一个简单的项目中可以,但是当测试套件增长时(500个左右),它开始成为一种负担。

我可以将update_account_balance方法公开并从控制器中调用它,但我认为它的业务逻辑并不属于控制器代码。

我已经尝试使用谷歌搜索解决方案,但无法找到解决方案。你们如何使用快速测试套件解决这个问题?

提前致谢。

1 个答案:

答案 0 :(得分:2)

我认为你的问题是你做了一个withdrawal隐式的动作(即它是通过创建Withdraw对象而发生的)

我认为有更好的方法。

account.withdraw!(1000)

代码可能如下所示。

class Account

  def withdraw!(amount)
    transaction do
      withdrawal = self.withdrawals.build(:amount => amount)

      self.subtract_balance(amount)

      withdrawal.save!
    end
  end

  private

  def subtract_balance(amount)
    connection.execute(
      "UPDATE #{self.class.table_name} SET balance = balance - #{amount} WHERE id = #{self.id}"
      )
  end
end