當 Product Code 涉及日期或時間邏輯(如定時任務、到期日等)時,通常會依據當前時間進行決策。然而,在撰寫 Test Cdoe 時,開發者經常面臨以下兩個挑戰:
測試結果的不穩定性
若不使用工具來凍結或控制時間,時間相關的邏輯測試可能變得不穩定。測試結果可能隨著每次執行的時間不同而發生變化。例如,當系統在不同日期處理相同的業務邏輯時,結果可能會因日期變動而產生偏差,導致測試難以重現。
依賴實際時間進行測試的耗時
在測試過程中,如果必須等待實際時間的流逝(例如等待某個定時器或任務觸發),將大大降低測試速度,既浪費時間,又使測試過程更難維護。
本文將介紹一個名為 Freezegun 的套件,它能凍結時間,讓開發者將時間固定在某個特定點上,從而確保時間相關的邏輯如預期般運行,而無需等待實際時間的經過,進一步提升測試的效率和穩定性。接下來,我將透過範例進行說明。
本次範例使用的是 Freezegun 1.5.1 版本
poetry add freezegun==1.5.1
首先,我們將準備一個 Product Code 函式,該函式會根據當前的時間點返回對應的字串。
from datetime import datetime
def get_greeting_based_on_time() -> str:
now = datetime.now()
if now.hour < 12:
return "Good Morning!"
elif 12 <= now.hour < 18:
return "Good Afternoon!"
else:
return "Good Evening!"
首先,讓我們看看尚未使用 Freezegun 的測試代碼版本。
當執行 poetry run pytest
時,由於測試結果依賴於執行的時間點,因此每位讀者可能會得到不同的結果。以筆者為例,我在晚間執行此測試,因此出現了如下錯誤: AssertionError: assert 'Good Evening!' == 'Good Morning!'
。
def test_get_greeting_based_on_time():
# Act
result = get_greeting_based_on_time()
# Assert
assert get_greeting_based_on_time == "Good Morning!"
我們可以使用 freezegun 套件來凍結時間。只需在需要凍結的測試函式上方添加 freeze_time
Decorator,並指定一個特定的時間點。在本次測試中,我選擇將時間凍結在2014年10月8日的早上8點。因此,當執行至函式 get_greeting_based_on_time
的 datetime.now()
時,返回的物件將是 datetime(2024-10-08 08:00:00)
,這樣每次測試的結果都將保持一致。
from freezegun import freeze_time
@freeze_time("2024-10-08 08:00:00")
def test_get_greeting_based_on_time():
# Act
result = get_greeting_based_on_time()
# Assert
assert get_greeting_based_on_time() == "Good Morning!"