为什么我的对象更新?

时间:2015-09-22 19:24:17

标签: ruby-on-rails activerecord minitest

我有这个测试:

 describe 'check_account_status' do
  it 'should send the correct reminder email one week prior to account disablement' do
  # Three weeks since initial email
  reverification = create(:reverification)
  initial_notification = reverification.twitter_reverification_sent_at.to_datetime
  ActionMailer::Base.deliveries.clear
  Timecop.freeze(initial_notification + 21) do
    Reverification.check_account_status
    ActionMailer::Base.deliveries.size.must_equal 1
    ActionMailer::Base.deliveries.first.subject.must_equal I18n.t('.account_mailer.one_week_left.subject')
    reverification.reminder_sent_at.class.must_equal ActiveSupport::TimeWithZone
    reverification.notification_counter.must_equal 1
    must_render_template 'reverification.html.haml'
  end
end

此测试会产生此错误:

check_account_status#test_0001_should send the correct reminder email one week prior to account disablement [/Users/drubio/vms/ohloh-ui/test/models/reverification_test.rb:67]:
Expected: ActiveSupport::TimeWithZone
Actual: NilClass

这是我的代码:

class Reverification < ActiveRecord::Base
  belongs_to :account

  FIRST_NOTIFICATION_ERROR = []

  class << self
    def check_account_status
      Reverification.where(twitter_reverified: false).each do |reverification|
        calculate_status(reverification)
        one_week_left(reverification)
      end
    end

    private

    def calculate_status(reverification)
      @right_now = Time.now.utc.to_datetime
      @initial_email_date = reverification.twitter_reverification_sent_at.to_datetime
      @notification_counter = reverification.notification_counter
    end

    def one_week_left(reverification)
    # Check to see if three weeks have passed since the initial email
    # and check to see if its before the one day notification before
    # marking an account as spam.
      if (@right_now.to_i >= (@initial_email_date + 21).to_i) && (@right_now.to_i < (@initial_email_date + 29).to_i)
        begin
          AccountMailer.one_week_left(reverification.account).deliver_now
        rescue
          FIRST_NOTIFICATION_FAILURE << account.id
          return
        end
        update_reverification_fields(reverification)
      end
    end

    def update_reverification_fields(reverification)
      reverification.notification_counter += 1
      reverification.reminder_sent_at = Time.now.utc
      reverification.save!
      reverification.reload
    end
  end

原谅缩进,但似乎是问题,是我的重新验证对象在离开check_account_status方法时没有更新。我已经在代码中放置了puts语句,我毫无疑问地看到重新验证对象确实正在更新。但是,在它离开update_reverification_fields并返回测试块后,字段不会更新。这是为什么?有没有人遇到过这个?

2 个答案:

答案 0 :(得分:1)

我相信你有一个范围问题,你从check_account_status调用的方法肯定不会将更新的对象返回给你的方法而只返回Ruby passes parameters by value

尝试这样的事情:

def check_account_status
  Reverification.where(twitter_reverified: false).each do |reverification|
    reverification = calculate_status(reverification)
    reverification = one_week_left(reverification)
  end
end

private 
  def calculate_status(reverification)
    # ...
    reverification
  end
  def one_week_left(reverification)
    # ...
    reverification = update_reverification_fields(reverification)
    reverification
  end
  def update_reverification_fields(reverification)
    # ...
    reverification
  end

答案 1 :(得分:1)

问题是测试中的重新验证对象和check_account_status内的对象是同一模型的不同实例。

def update_reverification_fields(reverification)
  reverification.notification_counter += 1
  reverification.reminder_sent_at = Time.now.utc
  reverification.save!
  reverification.reload
end

这里reload,它什么也没做。让我们来看看你的考试。

# check_account_status runs a DB query, finds some objects and does things to them
Reverification.check_account_status

# this expectation succeeds because you're accessing `deliveries` for the 
# first time and you don't have it cached. So you get the actual value
ActionMailer::Base.deliveries.size.must_equal 1

# this object, on the other hand, was instantiated before 
# `check_account_status` was called and, naturally, it doesn't see 
# the database changes that completely bypassed it.
reverification.reminder_sent_at.class.must_equal ActiveSupport::TimeWithZone

因此,在对reverification提出期望之前,请重新加载它,以便从数据库中提取最新数据。

reverification.reload # here
reverification.reminder_sent_at.class.must_equal ActiveSupport::TimeWithZone