Python為開發者提供了內建的單元測試框架 unittest
,它是一種強大的工具,能夠有效地編寫和執行單元測試。unittest
提供了完整的測試結構,支援自動化測試的執行,能夠對測試用例進行組織,並且提供了豐富的斷言方法。最終,unittest
會生成詳細的測試報告,這個框架非常簡單且易於使用。
unittest
核心概念
在 unittest
中,有四個核心概念:
- TestCase(測試用例):每個測試用例例項用於封裝一個或多個測試函式。
- TestSuite(測試套件):這是多個測試用例的集合,用於組織和執行多個測試用例。
- TestLoader(測試載入器):這是一個用於將測試用例載入到測試套件中的工具。
- TextTestRunner(測試執行器):這是用於執行測試用例的執行器,負責執行測試並生成結果報告。
- Fixture(環境管理機制):這是測試用例的環境搭建和銷燬部分,包括前置條件和後置條件。
unittest
的工作流程
- 編寫繼承自
unittest.TestCase
的測試用例類,其中每個測試函式都是一個獨立的測試用例。 - 使用
TestLoader
載入測試用例,並將它們組織成TestSuite
物件。 - 使用
TestRunner
執行TestSuite
中的測試用例,並輸出測試結果。
使用unittest
初級指南
- 匯入
unittest
模組以及被測試的檔案或類。 - 建立一個測試類,並繼承
unittest.TestCase
,所有自定義的單元測試類都要繼承它,作為基類。 - 重寫
setUp
和tearDown
方法,用於初始化和清理測試環境(如果有必要)。 - 定義測試函式,函式名以
test_
開頭,這樣才能被識別並執行。 - 在測試函式中使用斷言來判斷測試結果是否符合預期。
- 呼叫
unittest.main()
方法執行測試用例,按照函式名的排序執行測試。
以下是一個簡單的例子:
import unittest
def login(username, password):
if username == 'kira' and password == '123':
res = {"code": 200, "msg": "登入成功"}
return res
return {"code": 400, "msg": "登入失敗"}
class TestLogin(unittest.TestCase):
def test_login_success(self):
"""測試登入成功"""
test_data = {"username": "kira", "password": "test"}
expect_data = {"code": 200, "msg": "登入成功"}
res = login(**test_data)
self.assertEqual(res, expect_data)
def test_login_error_with_error_password(self):
"""賬號正確,密碼錯誤,登入失敗"""
test_data = {"username": "kira", "password": "12345"}
expect_data = {"code": 400, "msg": "登入失敗"}
res = login(**test_data)
self.assertEqual(res, expect_data)
# 更多測試函式類似...
if __name__ == '__main__':
unittest.main()
以上是一個簡單的測試用例,包含了兩個測試函式。執行指令碼將輸出測試結果。
unittest
核心概念
測試腳手架
測試腳手架 是測試用例的前置條件和後置條件,確保測試環境的初始化和清理,從而保證測試的準確性和可靠性。
import unittest
class MyTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
# 類級別的前置條件設定,整個類執行最先只執行一次
print("setUpClass")
@classmethod
def tearDownClass(cls):
# 類級別的後置條件清理,整個類執行最後結束執行一次
print("tearDownClass")
def setUp(self):
# 測試方法級別的前置條件設定,所有測試方法執行前都執行一次
print("setUp")
def tearDown(self):
# 測試方法級別的後置條件清理,所有測試方法執行結束都執行一次
print("tearDown")
def test_example(self):
# 測試用例
print("test_example")
if __name__ == "__main__":
unittest.main()
setUp()
:每個測試方法執行前執行,用於測試前置的初始化工作。tearDown()
:每個測試方法結束後執行,用於測試後的清理工作。setUpClass()
:所有的測試方法執行前執行,用於單元測試類執行前的準備工作。使用@classmethod
裝飾器裝飾,整個測試類執行過程中只會執行一次。tearDownClass()
:所有的測試方法結束後執行,用於單元測試類執行後的清理工作。使用@classmethod
裝飾器裝飾,整個測試類執行過程中只會執行一次。
測試用例
測試用例 是最小的測試單元,用於檢測特定的輸入集合的特定的返回值。unittest
提供了 TestCase
基類,所有的測試類都需要繼承該基類,而在該類下的函式如果以 test_
開頭,則被標識為測試函式:
class MyTestCase(unittest.TestCase
):
def test_addition(self):
result = 2 + 3
self.assertEqual(result, 5) # 使用斷言方法驗證結果是否相等
def test_subtraction(self):
result = 5 - 3
self.assertTrue(result == 2) # 使用斷言方法驗證結果是否為True
# 更多測試用例函式...
斷言方法
以下是常用的斷言方法:
assertEqual(a, b, msg=None)
:驗證 a 等於 b。assertNotEqual(a, b)
:驗證 a 不等於 b。assertTrue(x)
:驗證 x 是否為 True。assertFalse(x)
:驗證 x 是否為 False。assertIs(a, b)
:驗證 a 是否是 b。assertIsNot(a, b)
:驗證 a 是否不是 b。assertIsNone(x)
:驗證 x 是否為 None。assertIsNotNone(x)
:驗證 x 是否不為 None。assertIn(a, b)
:驗證 a 是否在 b 中。assertNotIn(a, b)
:驗證 a 是否不在 b 中。assertIsInstance(a, b)
:驗證 a 是否是 b 型別的例項。assertNotIsInstance(a, b)
:驗證 a 是否不是 b 型別的例項。
可以使用這些方法進行斷言,也可以直接使用原生的assert來斷言,如果斷言失敗,測試用例會被定義為執行失敗。
忽略特定測試方法
unittest
提供了一些方法來跳過特定的測試用例:
@unittest.skip(reason)
:強制跳過,reason 是跳過的原因。@unittest.skipIf(condition, reason)
:當 condition 為 True 時跳過。@unittest.skipUnless(condition, reason)
:當 condition 為 False 時跳過。@unittest.expectedFailure
:如果測試失敗,這個測試用例不會計入失敗的統計。- 使用例項方法:
self.skipTest()
使用和上述類似。
import sys
import unittest
class Test1(unittest.TestCase):
@unittest.expectedFailure # 即使失敗也會被計為成功的用例
def test_1(self):
assert 1 + 1 == 3
@unittest.skip('無條件跳過') # 不管什麼情況都會進行跳過
def test_2(self):
print("2+2...", 4)
@unittest.skipIf(sys.platform == "win32", "跳過") # 如果系統平臺為 Windows 則跳過
def test_3(self):
print("3+3...", 6)
@unittest.skipUnless(sys.platform == "win32", "跳過") # 除非系統平臺為 Windows,否則跳過
def test_4(self):
print("4+4...", 8)
def test_5(self):
self.skipTest("跳過")
print("5+5...", 10)
if __name__ == "__main__":
unittest.main(verbosity=2)
測試套件
測試套件用於收集和組織多個測試用例,便於集中執行。
- 透過
unittest.main()
方法直接載入單元測試的測試模組,這是一種簡單的載入方式。所有測試用例的執行順序按照方法名的字串表示的 ASCII 碼升序排序,透過命名時使用test_01_xxx
來指定執行順序。 - 將所有的單元測試用例
TestCase
載入到測試套件Test Suite
集合中,然後一次性載入所有測試物件。
透過 TestSuite
物件收集
此方式適用於需要自定義組合特定測試用例的情況。
import unittest
class MyTestCase(unittest.TestCase):
def test_addition(self):
result = 2 + 3
self.assertEqual(result, 5)
def suite():
suite = unittest.TestSuite()
suite.addTest(MyTestCase('test_addition'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
透過 TestLoader
物件收集
TestLoader
是 unittest
框架提供的載入測試用例的類。
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自動載入當前模組中所有以 'test_' 開頭的測試用例函式
suite = loader.loadTestsFromModule(__name__)
runner = unittest.TextTestRunner()
runner.run(suite)
import unittest
class MyTestCase(unittest.TestCase):
def test_addition(self):
result = 2 + 3
self.assertEqual(result, 5)
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自動載入 MyTestCase 類中的所有測試用例
suite = loader.loadTestsFromTestCase(MyTestCase)
runner = unittest.TextTestRunner()
runner.run(suite)
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自動載入指定名稱的測試用例
suite = loader.loadTestsFromName('module.MyTestCase.test_addition')
runner = unittest.TextTestRunner()
runner.run(suite)
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自動發現並載入指定目錄中的測試用例模組
suite = loader.discover(start_dir='test_directory', pattern='test_*.py', top_level_dir=None)
runner = unittest.TextTestRunner()
runner.run(suite)
測試執行器
測試執行器是用於執行和輸出測試結果的元件。常用的執行器有:
unittest.TextTestRunner
:這是unittest
框架中預設的測試執行器,會在命令列輸出測試結果。透過呼叫run()
方法執行測試套件,並將測試結果列印到控制檯。
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
runner = unittest.TextTestRunner()
result = runner.run(suite)
HTMLTestRunner
:這是一個第三方庫,能夠生成漂亮的 HTML 測試報告,需要進行安裝。你可以透過搜尋獲取相關檔案進行安裝。
import unittest
from HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
with open('test_report.html', 'wb') as report_file:
runner = HTMLTestRunner(stream=report_file, title='Test Report', description='Test Results')
result = runner.run(suite)
XMLTestRunner
:這是另一個第三方庫,用於生成 XML 格式的測試報告。
import unittest
from xmlrunner import XMLTestRunner
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
with open('test_report.xml', 'wb') as report_file:
runner = XMLTestRunner(output=report_file)
result = runner.run(suite)
你也可以自定義測試執行器。繼承 unittest.TestRunner
類並實現 run()
方法,以建立自己的測試執行器。
import unittest
class MyTestRunner(unittest.TextTestRunner):
def run(self, test):
print("Running tests with MyTestRunner")
result = super().run(test)
return result
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
runner = MyTestRunner()
result = runner.run(suite)
通常使用 HTMLTestRunner
即可滿足需求,它非常易用。
實戰一個測試案例
假設有一個測試函式 login
:
# login.py
def login(username, password):
"""模擬登入校驗"""
if username == 'kira' and password == '123456':
return {"code": 0, "msg": "登入成功"}
else:
return {"code": 1, "msg": "賬號或密碼不正確"}
設計用例
根據函式的引數和邏輯,設計如下用例:
序號 | 標題 | 測試資料 | 預期結果 | 實際結果 |
---|---|---|---|---|
1 | 賬號密碼正確 | {"username": "kira", "password": "123456"} | {"code": 0, "msg": "登入成功"} | |
2 | 賬號正確密碼不正確 | {"username": "kira", "password": "123"} | {"code": 1, "msg": "賬號或密碼不正確"} | |
3 | 賬號錯誤密碼正確 | {"username": "kir", "password": "123456"} | {"code": 1, "msg": "賬號或密碼不正確"} |
編寫測試用例並執行
import unittest
from login import login
class TestLogin(unittest.TestCase):
def test_login_correct(self):
"""測試賬號密碼正確"""
test_data = {"username": "kira", "password": "123456"}
expect_data = {"code": 0, "msg": "登入成功"}
res = login(**test_data)
self.assertEqual(res, expect_data)
def test_login_wrong_password(self):
"""測試賬號正確密碼不正確"""
test_data = {"username": "kira", "password": "123"}
expect_data = {"code": 1, "msg": "賬號或密碼不正確"}
res = login(**test_data)
self.assertEqual(res, expect_data)
def test_login_wrong_username(self):
"""測試賬號錯誤密碼正確"""
test_data = {"username": "kir", "password": "123456"}
expect_data = {"code": 1, "msg": "賬號或密碼不正確"}
res = login(**test_data)
self.assertEqual(res, expect_data)
if __name__ == '__main__':
unittest.main()
這是一個簡單的測試用例,包含了三個測試函式。執行測試用例後,會輸出測試結果,看完是否覺得unittest非常簡單易用。ner.run(suite)
最後感謝每一個認真閱讀我文章的人,禮尚往來總是要有的,這些資料,對於【軟體測試】的朋友來說應該是最全面最完整的備戰倉庫,雖然不是什麼很值錢的東西,如果你用得到的話可以直接拿走: