2015-09-22 35 views
0

我有這樣的測試:爲什麼我的對象沒有更新?

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並返回到測試塊後,字段不會更新。這是爲什麼?有沒有人遇到過這個?

回答

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 
+0

感謝您的回覆sjagr。您能否看看我對重新驗證代碼的編輯。我意識到縮進非常重要,因爲我將方法定義爲Class方法而非實例方法。這應該澄清範圍問題。 –

+0

@DanRubio它仍然是一個範圍界定問題 – sjagr

+0

是的,你是對的。我現在看到它。謝謝。 –

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 
+0

也是一個很好的解決方案 - 但可以覆蓋症狀。例如,如果'one_week_left'在將來從'calculate_status'取決於更新的'reverification',那麼'one_week_left'可能會產生不準確的結果。 – sjagr

+0

@sjagr:是的,那部分代碼太可怕了。根本不需要將驗證作爲參數。只要做'自我'的事情。但是這種類型的錯誤是相當普遍的(忘記刷新被測試的對象) –

+0

@sjagr:當我們看到它時,我不明白你的答案會如何解決他的規範:)(你知道,問題) –

相關問題