2012-12-19 44 views
6

鑑於一個典型的ActiveRecord模型,我經常有before_save回調解析輸入,例如從用戶採取類似time_string和解析它到time字段。Rails:如何測試before_save回調

即設置看起來像這樣:

before_save :parse_time 
attr_writer :time_string 

private 
def parse_time 
    time = Chronic.parse(time_string) if time_string 
end 

據我所知,它被認爲是最好的做法,使回調方法私有。但是,如果他們是私人的,那麼你不能單獨調用他們來單獨測試他們。

因此,對於你來說,經驗豐富的Rails測試人員,你如何處理測試這種事情?

+0

你在哪裏使用'time'變量?是你的對象的屬性? –

+0

上面的例子是一種捏造,但是,時間變量是一個對象屬性。 – Andrew

回答

9

在Ruby中,私有方法仍然可以通過Object#send

你可以利用這個爲你的單元測試,像這樣:

project = Project.new 
project.time_string = '2012/11/19 at Noon' 
assert_equal(project.send(:parse_time), '2012-11-19 12:00:00') 
+0

有趣的是,我沒有意識到'#send'的工作方式。謝謝! – Andrew

3

我會做的是保存newbuild實例的狀態將對象保存並根據已更改屬性的值進行斷言或期望before_save

post = Post.new 
post.time_string = '2012/11/19' 
expected_time = Chronic.parse(post.time_string) 
post.save 
assert_equal(post.time, expected_time) 

這樣你就可以測試對象應該如何操作的行爲,而不一定是方法的實現。

+2

好的,但這有兩個主要缺點:(1)速度慢。 (2)'#save'運行所有的回調函數,並且如果另外一個回調錯誤超出了測試的範圍,那麼也會失敗,即使它可能正在工作。 – Andrew

+0

我也喜歡這種方式,因爲只要結束狀態是正確的,它的內部工作方式應該沒有問題,但是直接測試方法有很多優點(速度,覆蓋率和理解力)。 – Unixmonkey

+0

回覆:測試行爲與實現,我明白你的意思,但這裏的目標是單獨測試方法的行爲,而不是覆蓋多個行爲的集成測試。單獨測試方法並不意味着您必須測試實現而不是行爲。 – Andrew

0

有些時候,我的回調中有if條件,在這種情況下我使用run_callbacks

before_save :parse_time, :if => Proc.new{ |post| post.foo == 'bar' } 

post = Post.new 
post.foo = 'bar' 
expected_time = Chronic.parse(post.time_string) 
post.run_callbacks :before_save 
assert_equal(post.time, expected_time) 

和帶負正測試通過

post = Post.new 
post.foo = 'wha?' 
post.run_callbacks :before_save 
assert_nil(post.time) 

the APIa blog的更多細節。