在Ruby on Rails測試環境凍結當下時間

icelandcheng
3 min readApr 5, 2019

--

試想以下情境,今天我們做出了一個功能:

在後台設定遊戲的維修時間,前台的遊戲就會依設定的時間自動切換狀態 。

若我們想自己寫一個測試程式來測試一下功能是不是正常,基本的做法可能就是先在測試環境設一個假的維修時間,然後抓取當下的時間來判定遊戲要不要切維修狀態,但這樣過了我們造假的維修時間後,就再也測不到前台頁面有沒有依維修時間切狀態了,因爲當下的時間是一直改變了。

所以若想讓測試程式是可以一直測試到依維修時間切換狀態的功能,如果可以,應該會想把時間凍結在維修時間範圍內。

希望將當下的時間凍結住:

如果我們可以把時間凍結在維修時間範圍內,這樣每次跑測試程式的時候,就可以一直測試這個功能了。

如何把時間凍住呢,首先,我們可能會先聯想到Time.now或是Time.current這兩個方法,這兩個方法基本上都是用來得到當下時間的方法,在Ruby的世界裡,Time是一個類別物件,他本身具備了許多方法,now跟current就是其中的內建類別方法,讓我們忽見的時候就可以得到當下的時間,所以想要把時間凍住的話,可以從這邊著手,把current或now方法原本是抓當下時間覆蓋成我們想要的時間就可以了。

可採用方法:

1. Rspec提供基本的凍結Time.current方法

current = Time.parse('2019-02-18 11:24:59')
allow(Time).to receive(:current) { current }

所以我們只要在我們做測試的每一個example都先執行上面這兩行程式碼,這樣在我們的測試環境中,Time.current都會是我們設定的時間日期了!

RSpec.describe ChangeCompanyGamesStatusService do.........describe 'during maintenance time, change status' do
before {
current = Time.parse('2019-02-18 11:24:59')
allow(Time).to receive(:current) { current }
}
it 'is_maintenance is true' do.........end

2. 使用gem — time-warp

其他還有像time-warp這個gem,也提供了方法,讓我們在測試的時候,呼叫pretend_now_is這個方法來凍結著當下時間

pretend_now_is(2000,"jan",1,0) do
Time.now
end

3. 寫一個Time類別物件並在裡面寫一個currrent 的類別方法

我們也可以直接在測試程式中,直接寫一個Time的類別物件,然後將原本的current方法蓋掉

before {
class Time
def self.current
'2019-02-18 11:24:59'
end
end
}

結論

要採用哪一種方法,主要端看我們想要凍結時間的需求是什麼,基本上,採用內建的方法都很便利,但如果有比較特殊的需求,當然還是自行寫一個類別物件然後撰寫方法最便利。

參考文件:

How to fake Time.now?

ActiveSupport::Testing::TimeHelpers

time-wrap gem

--

--

icelandcheng
icelandcheng

Written by icelandcheng

Programming Skill learner and Sharer | Ruby on Rails | Golang | Vue.js | Web Map API

No responses yet