學了一陣子實作單元測試後, 先前是前輩會先把單元測試的架構寫好讓我去實作code
但前輩走了之後,現在的單元測試已經無法符合新開發的功能,必須去研究新的方法來稍微改寫框架,這篇主要紀錄新學習的單元測試相關的功能 動態生成單元測試
及 設置測試環境, 移除環境
我使用IDE 為 PyCharm
, 單元測試的套件為 unittest
, 語言 Python
動態生成單元測試
大部分撰寫單元測試的框架大致如下
from unittest import TestCase
class MyNewTest(TestCase):
def test_function1(self):
pass
def test_function2(self):
pass
def test_function3(self):
pass
做一個可被執行的測試函式,函式名稱開頭需為 test_
通常這樣寫 IDE 很聰明的話會顯示 綠色箭頭
,接著就可以點箭頭跑測試
不然就是在terminal 下 command line 驅動單元測試
但是當我們有一些資料想要透過for 迴圈 根據值來做測試該怎麼辦?
我起初的想法就是寫for迴圈測試
def test_function(self):
for a in data:
result = get_result(a)
assertEqual(a, b)
但這樣的方法就是萬一迴圈內的某筆assert測試錯誤,整個單元測試就停住,將不會再往下測!
可是,我想要知道我迴圈內丟進去每個資料測試的狀態,那可否動態生成單元測試?
可以,而且方法很簡單(但我用錯方法找錯方向卡了四天qqq)
根據 Python unittest文件,可以使用subtest
來達成功能
您可以在網站內搜尋 i=i
來找到範例函式
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
這個範例是來驗證 0 - 6
每個數字是否可被2
整除
那我們就來改寫一下
from unittest import TestCase
class MyNewTest(TestCase):
def test_function(self):
csv_path = "./test_data.csv"
df = pd.read_csv(csv_path)
for index, row in df.iterrows():
# 可以根據值來命名單元測試的名稱唷!
with self.subTest(row['title']):
# 測試的code實作在這
result = get_result(row['test_input'])
assertEqual(expect, result)
如此一來 run 出的單元測試就可以根據想測的資料筆數來動態生成啦!
設置測試環境, 移除環境
另外一個是設置測試環境跟移除
因為測試要不留任何痕跡,而且執行單元測試時,需要先將測試功能的場景給佈置好
假設我有三個測試函式都需要設置環境跟移除環境
from unittest import TestCase
class MyNewTest(TestCase):
# 只要繼承TestCase 就可以使用setUp, tearDown 他們是unittest內附有的功能
def setUp(self):
# 可以寫任何你想先設置環境的事情
prepare(situation, value)
def tearDown(self):
# 結束後有沒有要刪除或者移除什麼
os.remove("test.sqlite")
def test_function1(self):
pass
def test_function2(self):
pass
def test_function3(self):
pass
這樣的寫法每當我跑一個測試函式就會執行setUp
幫我佈置環境, 結束後會 testDown
移除環境
若整個Class 一起執行會跑的順序是:
setUp > test_function1 > tearDown >
setUp > test_function2 > tearDown >
setUp > test_function3 > tearDown
可是!!! 就是有這個 可是
!
若只想佈置一次測試環境就測全部的測試函式, 全部測試測完在移除環境呢?
我們可以使用 unittest 內附的 setUpClass
, tearDownClass
來實現夢想XD
這是在unittest 套件內找到的方法
@classmethod
def setUpClass(cls) -> None: ...
@classmethod
def tearDownClass(cls) -> None: ...
所以剛剛的例子可以是:
from unittest import TestCase
class MyNewTest(TestCase):
# 只要繼承TestCase 就可以使用setUpClass, tearDownClass
@classmethod
def setUpClass(cls):
# 可以寫任何你想先設置環境的事情
prepare(situation, value)
@classmethod
def tearDownClass(cls):
# 結束後有沒有要刪除或者移除什麼
os.remove("test.sqlite")
def test_function1(self):
pass
def test_function2(self):
pass
def test_function3(self):
pass
執行的順序就會是
setUpClass > test_function1 > test_function2 >
test_function3 > tearDownClass
可以參考 Stackoverflow 的回答來得知setup/teardown的方法有什麼不同
你也可以進入python unittest的套件來了解哪寫函式功能可以使用
那這篇的介紹就結束囉!