unittest
— 單元測試框架¶
(如果你已經熟悉測試的基本概念,你可能想跳到斷言方法列表。)
unittest
單元測試框架最初受到 JUnit 啟發,與其他語言的主要單元測試框架有著相似的風格。它支援測試自動化、測試設定和清理程式碼的共享、將測試聚合到集合中,以及測試與報告框架的獨立性。
為了實現這一點,unittest
以面向物件的方式支援一些重要的概念
- 測試夾具
測試夾具 表示執行一個或多個測試所需的準備工作,以及任何相關的清理操作。這可能涉及,例如,建立臨時或代理資料庫、目錄,或啟動伺服器程序。
- 測試用例
測試用例 是測試的最小單元。它檢查對特定輸入集的一種特定響應。
unittest
提供了一個基類TestCase
,可用於建立新的測試用例。- 測試套件
測試套件 是測試用例、測試套件或兩者的集合。它用於聚合應一起執行的測試。
- 測試執行器
測試執行器 是一個元件,它協調測試的執行並將結果提供給使用者。執行器可以使用圖形介面、文字介面,或返回一個特殊值來指示執行測試的結果。
參見
- 模組
doctest
另一個風格迥異的測試支援模組。
- Simple Smalltalk Testing: With Patterns
Kent Beck 關於使用
unittest
共享模式的測試框架的原始論文。- pytest
第三方單元測試框架,具有更輕量級的測試編寫語法。例如,
assert func(10) == 42
。- The Python Testing Tools Taxonomy
一份包含功能測試框架和模擬物件庫的 Python 測試工具的詳盡列表。
- Python 測試郵件列表
一個關於 Python 中的測試和測試工具討論的特殊興趣小組。
Python 原始碼分發中的指令碼 Tools/unittestgui/unittestgui.py
是一個用於測試發現和執行的 GUI 工具。這主要是為了方便新接觸單元測試的使用者。對於生產環境,建議由持續整合系統驅動測試,例如 Buildbot、Jenkins、GitHub Actions 或 AppVeyor。
基本示例¶
unittest
模組提供了一套豐富的工具來構建和執行測試。本節演示了一小部分工具足以滿足大多數使用者的需求。
這是一個用於測試三個字串方法的簡短指令碼
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
透過繼承 unittest.TestCase
建立一個測試用例。三個獨立的測試透過名稱以字母 test
開頭的方法定義。這個命名約定會告知測試執行器哪些方法代表測試。
每個測試的核心是呼叫 assertEqual()
來檢查預期結果;assertTrue()
或 assertFalse()
來驗證條件;或 assertRaises()
來驗證是否引發了特定異常。這些方法替代了 assert
語句,以便測試執行器可以累積所有測試結果並生成報告。
setUp()
和 tearDown()
方法允許您定義將在每個測試方法之前和之後執行的指令。它們將在 組織測試程式碼 一節中更詳細地介紹。
最後一段展示了執行測試的簡單方法。unittest.main()
為測試指令碼提供了命令列介面。從命令列執行上述指令碼會產生如下輸出
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
向測試指令碼傳遞 -v
選項會指示 unittest.main()
啟用更高的詳細級別,併產生以下輸出
test_isupper (__main__.TestStringMethods.test_isupper) ... ok
test_split (__main__.TestStringMethods.test_split) ... ok
test_upper (__main__.TestStringMethods.test_upper) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
以上示例展示了最常用的 unittest
功能,足以滿足許多日常測試需求。文件的其餘部分將從基本原理開始探討完整的功能集。
3.11 版本發生變化: 測試方法返回非預設 None
值的行為現已棄用。
命令列介面¶
unittest 模組可以在命令列中使用,從模組、類甚至單個測試方法執行測試
python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
你可以傳入一個列表,其中包含模組名稱、完全限定的類名或方法名的任意組合。
測試模組也可以透過檔案路徑指定
python -m unittest tests/test_something.py
這允許您使用 shell 檔名補全來指定測試模組。指定的檔案仍然必須可以作為模組匯入。路徑透過刪除“.py”並將路徑分隔符轉換為“.”來轉換為模組名稱。如果您想執行一個不能作為模組匯入的測試檔案,您應該直接執行該檔案。
您可以透過傳入 -v 標誌來執行更詳細(更高詳細程度)的測試
python -m unittest -v test_module
在不帶引數執行時,將啟動 測試發現
python -m unittest
有關所有命令列選項的列表
python -m unittest -h
3.2 版本發生變化: 在早期版本中,只能執行單個測試方法,而不能執行模組或類。
3.14 版本新增: 輸出預設是彩色的,並且可以使用環境變數控制。
命令列選項¶
unittest 支援以下命令列選項
- -b, --buffer¶
標準輸出和標準錯誤流在測試執行期間被緩衝。透過測試時的輸出將被丟棄。在測試失敗或錯誤時,輸出將正常回顯並新增到失敗訊息中。
- -c, --catch¶
在測試執行期間,Control-C 將等待當前測試結束,然後報告目前為止的所有結果。第二次 Control-C 將引發正常的
KeyboardInterrupt
異常。有關提供此功能的函式,請參閱 訊號處理。
- -f, --failfast¶
在第一個錯誤或失敗時停止測試執行。
- -k¶
只執行與模式或子字串匹配的測試方法和類。此選項可以多次使用,在這種情況下,所有匹配給定模式的測試用例都將被包含。
包含萬用字元 (
*
) 的模式將使用fnmatch.fnmatchcase()
與測試名稱進行匹配;否則使用簡單的區分大小寫的子字串匹配。模式將與測試載入器匯入的完全限定測試方法名稱進行匹配。
例如,
-k foo
匹配foo_tests.SomeTest.test_something
、bar_tests.SomeTest.test_foo
,但不匹配bar_tests.FooTest.test_something
。
- --locals¶
在回溯中顯示區域性變數。
- --durations N¶
顯示最慢的 N 個測試用例(N=0 表示所有)。
3.2 版本新增: 添加了命令列選項 -b
、-c
和 -f
。
3.5 版本新增: 命令列選項 --locals
。
3.7 版本新增: 命令列選項 -k
。
3.12 版本新增: 命令列選項 --durations
。
命令列也可以用於測試發現,執行專案中的所有測試或只執行一部分。
測試發現¶
在 3.2 版本加入。
Unittest 支援簡單的測試發現。為了與測試發現相容,所有測試檔案必須是可從專案頂層目錄匯入的模組或包(這意味著它們的 檔名 必須是有效的識別符號)。
測試發現是在 TestLoader.discover()
中實現的,但也可以從命令列使用。基本的命令列用法是
cd project_directory
python -m unittest discover
備註
作為快捷方式,python -m unittest
等同於 python -m unittest discover
。如果您想向測試發現傳遞引數,則必須顯式使用 discover
子命令。
discover
子命令有以下選項
- -v, --verbose¶
詳細輸出
- -s, --start-directory directory¶
開始發現的目錄(預設
.
)
- -p, --pattern pattern¶
匹配測試檔案的模式(預設
test*.py
)
- -t, --top-level-directory directory¶
專案的頂層目錄(預設為起始目錄)
-s
、-p
和 -t
選項可以按此順序作為位置引數傳入。以下兩個命令列是等效的
python -m unittest discover -s project_directory -p "*_test.py"
python -m unittest discover project_directory "*_test.py"
除了路徑之外,還可以將包名稱(例如 myproject.subpackage.test
)作為起始目錄傳遞。然後,您提供的包名稱將被匯入,並且其在檔案系統上的位置將用作起始目錄。
注意
測試發現透過匯入來載入測試。一旦測試發現從您指定的起始目錄中找到所有測試檔案,它將把這些路徑轉換為包名以進行匯入。例如,foo/bar/baz.py
將被匯入為 foo.bar.baz
。
如果您全域性安裝了一個包,並嘗試在包的不同副本上進行測試發現,則匯入*可能*會從錯誤的位置發生。如果發生這種情況,測試發現會警告您並退出。
如果您將起始目錄作為包名稱而不是目錄路徑提供,那麼發現器會假定它從哪個位置匯入就是您想要的位置,因此您不會收到警告。
測試模組和包可以透過 load_tests 協議 自定義測試載入和發現。
3.4 版本發生變化: 測試發現支援名稱空間包。
3.11 版本發生變化: 測試發現取消了名稱空間包支援。自 Python 3.7 以來,它一直存在問題。包含測試的起始目錄及其子目錄必須是包含 __init__.py
檔案的常規包。
如果起始目錄是包的點分名稱,則祖先包可以是名稱空間包。
3.14 版本發生變化: 測試發現再次支援將名稱空間包作為起始目錄。為了避免掃描與 Python 無關的目錄,不會在不包含 __init__.py
的子目錄中搜索測試。
組織測試程式碼¶
單元測試的基本構成塊是 測試用例 — 必須設定並檢查正確性的單個場景。在 unittest
中,測試用例由 unittest.TestCase
例項表示。要建立自己的測試用例,您必須編寫 TestCase
的子類,或使用 FunctionTestCase
。
TestCase
例項的測試程式碼應完全自包含,以便它可以在隔離狀態下執行,或與任意數量的其他測試用例任意組合執行。
最簡單的 TestCase
子類將僅實現一個測試方法(即名稱以 test
開頭的方法)以執行特定的測試程式碼
import unittest
class DefaultWidgetSizeTestCase(unittest.TestCase):
def test_default_widget_size(self):
widget = Widget('The widget')
self.assertEqual(widget.size(), (50, 50))
請注意,為了測試某些東西,我們使用 TestCase
基類提供的 assert* 方法 之一。如果測試失敗,將引發一個帶有解釋訊息的異常,並且 unittest
會將該測試用例標識為 失敗。任何其他異常都將被視為 錯誤。
測試可能很多,而且它們的設定可能重複。幸運的是,我們可以透過實現一個名為 setUp()
的方法來提取設定程式碼,測試框架將自動為我們執行的每一個測試呼叫它。
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def test_default_widget_size(self):
self.assertEqual(self.widget.size(), (50,50),
'incorrect default size')
def test_widget_resize(self):
self.widget.resize(100,150)
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
備註
各種測試的執行順序由根據字串的內建排序對測試方法名稱進行排序來決定。
如果在測試執行期間 setUp()
方法引發異常,則框架將認為測試發生了錯誤,並且測試方法將不會執行。
同樣,我們可以提供一個 tearDown()
方法,在測試方法執行後進行清理工作
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
如果 setUp()
成功,則無論測試方法成功與否,tearDown()
都將執行。
這樣的測試程式碼工作環境被稱為 測試夾具。將建立一個新的 TestCase 例項作為獨特的測試夾具,用於執行每個單獨的測試方法。因此,setUp()
、tearDown()
和 __init__()
將為每個測試呼叫一次。
建議您使用 TestCase 實現根據其測試的功能將測試分組在一起。unittest
為此提供了一個機制:測試套件,由 unittest
的 TestSuite
類表示。在大多數情況下,呼叫 unittest.main()
將做正確的事情,併為您收集模組的所有測試用例並執行它們。
但是,如果您想自定義測試套件的構建,您可以自己完成
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_widget_size'))
suite.addTest(WidgetTestCase('test_widget_resize'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
您可以將測試用例和測試套件的定義放在與它們要測試的程式碼相同的模組中(例如 widget.py
),但將測試程式碼放在單獨的模組中(例如 test_widget.py
)有幾個優點
測試模組可以從命令列獨立執行。
測試程式碼可以更容易地與已釋出的程式碼分離。
沒有充分理由,修改測試程式碼以適應其測試的程式碼的誘惑更小。
測試程式碼的修改頻率應遠低於其測試的程式碼。
被測試的程式碼可以更容易地重構。
用 C 語言編寫的模組的測試無論如何都必須放在單獨的模組中,所以為什麼不保持一致呢?
如果測試策略發生變化,則無需更改原始碼。
重用舊測試程式碼¶
一些使用者會發現他們有現有的測試程式碼,他們希望從 unittest
執行,而無需將每個舊的測試函式轉換為 TestCase
子類。
因此,unittest
提供了一個 FunctionTestCase
類。這個 TestCase
的子類可以用來包裝一個現有的測試函式。也可以提供設定和拆卸函式。
給定以下測試函式
def testSomething():
something = makeSomething()
assert something.name is not None
# ...
可以如下建立等效的測試用例例項,並帶有可選的設定和拆卸方法
testcase = unittest.FunctionTestCase(testSomething,
setUp=makeSomethingDB,
tearDown=deleteSomethingDB)
備註
儘管 FunctionTestCase
可以用來快速將現有測試基礎轉換為基於 unittest
的系統,但不建議採用這種方法。花時間設定適當的 TestCase
子類將使未來的測試重構變得無限容易。
在某些情況下,現有測試可能已使用 doctest
模組編寫。如果是這樣,doctest
提供了一個 DocTestSuite
類,可以從現有的基於 doctest
的測試中自動構建 unittest.TestSuite
例項。
跳過測試和預期失敗¶
在 3.1 版本加入。
Unittest 支援跳過單個測試方法甚至整個測試類。此外,它支援將測試標記為“預期失敗”,即一個已損壞但會失敗的測試,但不應在 TestResult
上計為失敗。
跳過測試只需使用 skip()
裝飾器 或其條件變體之一,在 setUp()
或測試方法中呼叫 TestCase.skipTest()
,或直接引發 SkipTest
。
基本跳過看起來像這樣
class MyTestCase(unittest.TestCase):
@unittest.skip("demonstrating skipping")
def test_nothing(self):
self.fail("shouldn't happen")
@unittest.skipIf(mylib.__version__ < (1, 3),
"not supported in this library version")
def test_format(self):
# Tests that work for only a certain version of the library.
pass
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_windows_support(self):
# windows specific testing code
pass
def test_maybe_skipped(self):
if not external_resource_available():
self.skipTest("external resource not available")
# test code that depends on the external resource
pass
這是以詳細模式執行上述示例的輸出
test_format (__main__.MyTestCase.test_format) ... skipped 'not supported in this library version'
test_nothing (__main__.MyTestCase.test_nothing) ... skipped 'demonstrating skipping'
test_maybe_skipped (__main__.MyTestCase.test_maybe_skipped) ... skipped 'external resource not available'
test_windows_support (__main__.MyTestCase.test_windows_support) ... skipped 'requires Windows'
----------------------------------------------------------------------
Ran 4 tests in 0.005s
OK (skipped=4)
類可以像方法一樣跳過
@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
TestCase.setUp()
也可以跳過測試。當需要設定的資源不可用時,這很有用。
預期失敗使用 expectedFailure()
裝飾器。
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
透過建立一個在需要跳過測試時呼叫 skip()
的裝飾器,可以輕鬆建立自己的跳過裝飾器。這個裝飾器會跳過測試,除非傳入的物件具有某個特定屬性
def skipUnlessHasattr(obj, attr):
if hasattr(obj, attr):
return lambda func: func
return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))
以下裝飾器和異常實現了測試跳過和預期失敗
- @unittest.skip(reason)¶
無條件跳過被裝飾的測試。 *reason* 應該描述跳過測試的原因。
- @unittest.skipIf(condition, reason)¶
如果 *condition* 為真,則跳過被裝飾的測試。
- @unittest.skipUnless(condition, reason)¶
除非 *condition* 為真,否則跳過被裝飾的測試。
- @unittest.expectedFailure¶
將測試標記為預期失敗或錯誤。如果測試在測試函式本身(而不是在某個 測試夾具 方法中)失敗或報錯,則將其視為成功。如果測試透過,則將其視為失敗。
- exception unittest.SkipTest(reason)¶
此異常用於跳過測試。
通常,您可以使用
TestCase.skipTest()
或其中一個跳過裝飾器,而不是直接引發此異常。
被跳過的測試將不會執行 setUp()
或 tearDown()
。被跳過的類將不會執行 setUpClass()
或 tearDownClass()
。被跳過的模組將不會執行 setUpModule()
或 tearDownModule()
。
使用子測試區分測試迭代¶
在 3.4 版本加入。
當您的測試之間存在非常微小的差異時,例如一些引數,unittest 允許您使用 subTest()
上下文管理器在測試方法體內區分它們。
例如,以下測試
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)
將產生以下輸出
======================================================================
FAIL: test_even (__main__.NumbersTest.test_even) (i=1)
Test that numbers between 0 and 5 are all even.
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 11, in test_even
self.assertEqual(i % 2, 0)
^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest.test_even) (i=3)
Test that numbers between 0 and 5 are all even.
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 11, in test_even
self.assertEqual(i % 2, 0)
^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest.test_even) (i=5)
Test that numbers between 0 and 5 are all even.
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 11, in test_even
self.assertEqual(i % 2, 0)
^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0
如果不使用子測試,執行將在第一次失敗後停止,並且由於不顯示 i
的值,錯誤將更難診斷
======================================================================
FAIL: test_even (__main__.NumbersTest.test_even)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
類和函式¶
本節深入描述了 unittest
的 API。
測試用例¶
- class unittest.TestCase(methodName='runTest')¶
TestCase
類的例項表示unittest
領域中的邏輯測試單元。此類別旨在用作基類,具體測試由具體子類實現。此類別實現了測試執行器所需的介面,以允許其驅動測試,以及測試程式碼可用於檢查和報告各種型別失敗的方法。每個
TestCase
例項都將執行一個基本方法:名為 *methodName* 的方法。在大多數TestCase
用法中,您既不會更改 *methodName* 也不會重新實現預設的runTest()
方法。TestCase
例項提供三組方法:一組用於執行測試,另一組用於測試實現以檢查條件和報告失敗,以及一些允許收集測試本身資訊的查詢方法。第一組(執行測試)中的方法是
- setUp()¶
用於準備測試夾具的方法。它在呼叫測試方法之前立即呼叫;除了
AssertionError
或SkipTest
之外,此方法引發的任何異常都將被視為錯誤而不是測試失敗。預設實現不做任何事情。
- tearDown()¶
在測試方法呼叫並記錄結果後立即呼叫的方法。即使測試方法引發了異常,也會呼叫此方法,因此子類中的實現可能需要特別注意檢查內部狀態。此方法引發的任何異常(
AssertionError
或SkipTest
除外)都將被視為額外錯誤而不是測試失敗(因此會增加報告錯誤的總體數量)。此方法僅在setUp()
成功後才會被呼叫,無論測試方法的結果如何。預設實現不做任何事情。
- setUpClass()¶
在單個類中的測試執行之前呼叫的類方法。
setUpClass
以類作為唯一引數呼叫,並且必須裝飾為classmethod()
@classmethod def setUpClass(cls): ...
有關詳細資訊,請參閱 類和模組夾具。
在 3.2 版本加入。
- tearDownClass()¶
在單個類中的測試執行後呼叫的類方法。
tearDownClass
以類作為唯一引數呼叫,並且必須裝飾為classmethod()
@classmethod def tearDownClass(cls): ...
有關詳細資訊,請參閱 類和模組夾具。
在 3.2 版本加入。
- run(result=None)¶
執行測試,將結果收集到作為 *result* 傳遞的
TestResult
物件中。如果 *result* 省略或為None
,則會建立一個臨時結果物件(透過呼叫defaultTestResult()
方法)並使用。結果物件將返回給run()
的呼叫者。只需呼叫
TestCase
例項即可達到同樣的效果。3.3 版本發生變化: 早期版本的
run
不返回結果。呼叫例項也不返回結果。
- subTest(msg=None, **params)¶
返回一個上下文管理器,它將執行包含的程式碼塊作為子測試。 *msg* 和 *params* 是可選的任意值,當子測試失敗時會顯示出來,讓您可以清晰地識別它們。
一個測試用例可以包含任意數量的子測試宣告,並且它們可以任意巢狀。
有關詳細資訊,請參閱 使用子測試區分測試迭代。
在 3.4 版本加入。
- debug()¶
執行測試而不收集結果。這允許測試引發的異常傳播給呼叫者,並且可以用於支援在偵錯程式下執行測試。
TestCase
類提供了幾種斷言方法來檢查和報告失敗。下表列出了最常用的方法(更多斷言方法請參見下表)方法
檢查
新增於
a == b
a != b
bool(x) is True
bool(x) is False
a is b
3.1
a is not b
3.1
x is None
3.1
x is not None
3.1
a in b
3.1
a not in b
3.1
isinstance(a, b)
3.2
not isinstance(a, b)
3.2
issubclass(a, b)
3.14
not issubclass(a, b)
3.14
所有斷言方法都接受一個 *msg* 引數,如果指定,則在失敗時用作錯誤訊息(另請參閱
longMessage
)。請注意,*msg* 關鍵字引數只能在assertRaises()
、assertRaisesRegex()
、assertWarns()
、assertWarnsRegex()
用作上下文管理器時傳遞。- assertEqual(first, second, msg=None)¶
測試 *first* 和 *second* 是否相等。如果值不相等,測試將失敗。
此外,如果 *first* 和 *second* 是完全相同的型別,並且是列表、元組、字典、集合、不可變集合或字串之一,或者任何子類註冊到
addTypeEqualityFunc()
的型別,將呼叫型別特定的相等函式以生成更有用的預設錯誤訊息(另請參閱 型別特定方法列表)。3.1 版本發生變化: 添加了自動呼叫型別特定相等函式的功能。
3.2 版本發生變化:
assertMultiLineEqual()
作為比較字串的預設型別相等函式新增。
- assertNotEqual(first, second, msg=None)¶
測試 *first* 和 *second* 是否不相等。如果值相等,測試將失敗。
- assertTrue(expr, msg=None)¶
- assertFalse(expr, msg=None)¶
測試 *expr* 是否為真(或為假)。
請注意,這等同於
bool(expr) is True
,而不是expr is True
(後者使用assertIs(expr, True)
)。當有更具體的方法可用時(例如assertEqual(a, b)
而不是assertTrue(a == b)
),也應避免使用此方法,因為它們在失敗時提供更好的錯誤訊息。
- assertIs(first, second, msg=None)¶
- assertIsNot(first, second, msg=None)¶
測試 *first* 和 *second* 是否是(或不是)同一個物件。
在 3.1 版本加入。
- assertIn(member, container, msg=None)¶
- assertNotIn(member, container, msg=None)¶
測試 *member* 是否在(或不在) *container* 中。
在 3.1 版本加入。
- assertIsInstance(obj, cls, msg=None)¶
- assertNotIsInstance(obj, cls, msg=None)¶
測試 *obj* 是否是(或不是) *cls* 的例項(*cls* 可以是一個類或一個類元組,如
isinstance()
所支援)。要檢查精確型別,請使用assertIs(type(obj), cls)
。在 3.2 版本加入。
- assertIsSubclass(cls, superclass, msg=None)¶
- assertNotIsSubclass(cls, superclass, msg=None)¶
測試 *cls* 是否是(或不是) *superclass* 的子類(*superclass* 可以是一個類或一個類元組,如
issubclass()
所支援)。要檢查精確型別,請使用assertIs(cls, superclass)
。在 3.14 版本加入。
還可以使用以下方法檢查異常、警告和日誌訊息的生成
方法
檢查
新增於
fun(*args, **kwds)
引發 *exc*fun(*args, **kwds)
引發 *exc* 且訊息匹配正則表示式 *r*3.1
fun(*args, **kwds)
引發 *warn*3.2
fun(*args, **kwds)
引發 *warn* 且訊息匹配正則表示式 *r*3.2
with
塊在 *logger* 上記錄,最小級別為 *level*3.4
with
塊不記錄在在 *logger* 上,最小級別為 *level*
3.10
- assertRaises(exception, callable, *args, **kwds)¶
- assertRaises(exception, *, msg=None)
測試當呼叫 *callable* 時是否引發異常,同時將任何位置引數或關鍵字引數傳遞給
assertRaises()
。如果引發 *exception*,則測試透過;如果引發其他異常,則為錯誤;如果未引發異常,則測試失敗。要捕獲一組異常中的任何一個,可以將包含異常類的元組作為 *exception* 傳遞。如果只提供了 *exception* 和可選的 *msg* 引數,則返回一個上下文管理器,以便被測試的程式碼可以內聯編寫,而不是作為函式
with self.assertRaises(SomeException): do_something()
當用作上下文管理器時,
assertRaises()
接受額外的關鍵字引數 *msg*。上下文管理器將捕獲到的異常物件儲存在其
exception
屬性中。如果目的是對引發的異常執行額外的檢查,這會很有用with self.assertRaises(SomeException) as cm: do_something() the_exception = cm.exception self.assertEqual(the_exception.error_code, 3)
3.1 版本發生變化: 新增了將
assertRaises()
用作上下文管理器的功能。3.2 版本發生變化: 添加了
exception
屬性。3.3 版本發生變化: 當用作上下文管理器時,添加了 *msg* 關鍵字引數。
- assertRaisesRegex(exception, regex, callable, *args, **kwds)¶
- assertRaisesRegex(exception, regex, *, msg=None)
類似於
assertRaises()
,但也測試 *regex* 是否與引發異常的字串表示匹配。*regex* 可以是正則表示式物件,也可以是包含適合re.search()
使用的正則表示式的字串。示例self.assertRaisesRegex(ValueError, "invalid literal for.*XYZ'$", int, 'XYZ')
或
with self.assertRaisesRegex(ValueError, 'literal'): int('XYZ')
3.1 版本新增: 以
assertRaisesRegexp
名稱新增。3.2 版本發生變化: 重新命名為
assertRaisesRegex()
。3.3 版本發生變化: 當用作上下文管理器時,添加了 *msg* 關鍵字引數。
- assertWarns(warning, callable, *args, **kwds)¶
- assertWarns(warning, *, msg=None)
測試當呼叫 *callable* 時是否觸發警告,同時將任何位置引數或關鍵字引數傳遞給
assertWarns()
。如果觸發 *warning*,則測試透過;如果未觸發,則測試失敗。任何異常都是錯誤。要捕獲一組警告中的任何一個,可以將包含警告類的元組作為 *warnings* 傳遞。如果只提供了 *warning* 和可選的 *msg* 引數,則返回一個上下文管理器,以便被測試的程式碼可以內聯編寫,而不是作為函式
with self.assertWarns(SomeWarning): do_something()
當用作上下文管理器時,
assertWarns()
接受額外的關鍵字引數 *msg*。上下文管理器會將其捕獲到的警告物件儲存在其
warning
屬性中,並將觸發警告的源行儲存在filename
和lineno
屬性中。如果目的是對捕獲到的警告執行額外的檢查,這會很有用with self.assertWarns(SomeWarning) as cm: do_something() self.assertIn('myfile.py', cm.filename) self.assertEqual(320, cm.lineno)
此方法無論在呼叫時是否存在警告過濾器都能正常工作。
在 3.2 版本加入。
3.3 版本發生變化: 當用作上下文管理器時,添加了 *msg* 關鍵字引數。
- assertWarnsRegex(warning, regex, callable, *args, **kwds)¶
- assertWarnsRegex(warning, regex, *, msg=None)
類似於
assertWarns()
,但也測試 *regex* 是否與觸發警告的訊息匹配。*regex* 可以是正則表示式物件,也可以是包含適合re.search()
使用的正則表示式的字串。示例self.assertWarnsRegex(DeprecationWarning, r'legacy_function\(\) is deprecated', legacy_function, 'XYZ')
或
with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'): frobnicate('/etc/passwd')
在 3.2 版本加入。
3.3 版本發生變化: 當用作上下文管理器時,添加了 *msg* 關鍵字引數。
- assertLogs(logger=None, level=None)¶
一個上下文管理器,用於測試在 *logger* 或其子級上至少記錄了一條訊息,且級別至少為給定 *level*。
如果給定,*logger* 應該是一個
logging.Logger
物件,或者是一個表示記錄器名稱的str
。預設是根記錄器,它將捕獲所有未被非傳播子記錄器阻塞的訊息。如果給定,*level* 應該是一個數字日誌級別或其字串等效值(例如
"ERROR"
或logging.ERROR
)。預設是logging.INFO
。如果
with
塊內發出的至少一條訊息符合 *logger* 和 *level* 條件,則測試透過,否則失敗。上下文管理器返回的物件是一個記錄助手,它跟蹤匹配的日誌訊息。它有兩個屬性
- records¶
匹配日誌訊息的
logging.LogRecord
物件列表。
示例
with self.assertLogs('foo', level='INFO') as cm: logging.getLogger('foo').info('first message') logging.getLogger('foo.bar').error('second message') self.assertEqual(cm.output, ['INFO:foo:first message', 'ERROR:foo.bar:second message'])
在 3.4 版本加入。
- assertNoLogs(logger=None, level=None)¶
一個上下文管理器,用於測試在 *logger* 或其子級上沒有記錄訊息,且級別至少為給定 *level*。
如果提供,logger 應該是一個
logging.Logger
物件或一個表示記錄器名稱的str
。預設是根記錄器,它將捕獲所有訊息。如果給定,*level* 應該是一個數字日誌級別或其字串等效值(例如
"ERROR"
或logging.ERROR
)。預設是logging.INFO
。與
assertLogs()
不同,上下文管理器不會返回任何內容。在 3.10 版本加入。
還有其他方法用於執行更具體的檢查,例如
方法
檢查
新增於
round(a-b, 7) == 0
round(a-b, 7) != 0
a > b
3.1
a >= b
3.1
a < b
3.1
a <= b
3.1
r.search(s)
3.1
not r.search(s)
3.2
無論順序如何,a 和 b 具有相同數量的相同元素。
3.2
a.startswith(b)
3.14
not a.startswith(b)
3.14
a.endswith(b)
3.14
not a.endswith(b)
3.14
hastattr(a, b)
3.14
not hastattr(a, b)
3.14
- assertAlmostEqual(first, second, places=7, msg=None, delta=None)¶
- assertNotAlmostEqual(first, second, places=7, msg=None, delta=None)¶
透過計算差異,將結果四捨五入到給定的小數位數 places(預設 7),並與零進行比較,測試 first 和 second 大致(或不大致)相等。請注意,這些方法將值四捨五入到給定數量的 小數位數(即像
round()
函式一樣),而不是 有效數字。如果提供了 delta 而不是 places,則 first 和 second 之間的差異必須小於或等於(或大於)delta。
同時提供 delta 和 places 將引發
TypeError
。3.2 版中已更改:
assertAlmostEqual()
自動將比較相等的物件視為大致相等。assertNotAlmostEqual()
如果物件比較相等,則自動失敗。添加了 delta 關鍵字引數。
- assertGreater(first, second, msg=None)¶
- assertGreaterEqual(first, second, msg=None)¶
- assertLess(first, second, msg=None)¶
- assertLessEqual(first, second, msg=None)¶
根據方法名稱,測試 first 分別是 >、>=、< 或 <= second。如果不滿足,測試將失敗。
>>> self.assertGreaterEqual(3, 4) AssertionError: "3" unexpectedly not greater than or equal to "4"
在 3.1 版本加入。
- assertRegex(text, regex, msg=None)¶
- assertNotRegex(text, regex, msg=None)¶
測試 regex 搜尋匹配(或不匹配)text。如果失敗,錯誤訊息將包含模式和 text(或模式和 text 中意外匹配的部分)。regex 可以是正則表示式物件,也可以是包含適合
re.search()
使用的正則表示式的字串。3.1 版新功能: 以
assertRegexpMatches
的名稱新增。3.2 版中已更改: 方法
assertRegexpMatches()
已重新命名為assertRegex()
。3.2 版新功能:
assertNotRegex()
。
- assertCountEqual(first, second, msg=None)¶
測試序列 first 包含與 second 相同的元素,無論其順序如何。如果它們不相等,將生成一條錯誤訊息,列出序列之間的差異。
在比較 first 和 second 時,不會忽略重複元素。它會驗證每個元素在兩個序列中是否具有相同的計數。等同於:
assertEqual(Counter(list(first)), Counter(list(second)))
,但也可用於不可雜湊物件的序列。在 3.2 版本加入。
- assertStartsWith(s, prefix, msg=None)¶
- assertNotStartsWith(s, prefix, msg=None)¶
測試 Unicode 或位元組字串 s 是否以 prefix 開頭(或不以 prefix 開頭)。prefix 也可以是要嘗試的字串元組。
在 3.14 版本加入。
- assertEndsWith(s, suffix, msg=None)¶
- assertNotEndsWith(s, suffix, msg=None)¶
測試 Unicode 或位元組字串 s 是否以 suffix 結尾(或不以 suffix 結尾)。suffix 也可以是要嘗試的字串元組。
在 3.14 版本加入。
- assertHasAttr(obj, name, msg=None)¶
- assertNotHasAttr(obj, name, msg=None)¶
測試物件 obj 是否具有(或不具有)屬性 name。
在 3.14 版本加入。
assertEqual()
方法將同類型物件的相等性檢查分派給不同的型別特定方法。這些方法已為大多數內建型別實現,但也可以使用addTypeEqualityFunc()
註冊新方法- addTypeEqualityFunc(typeobj, function)¶
註冊一個由
assertEqual()
呼叫的型別特定方法,以檢查兩個完全相同 typeobj(非子類)的物件是否相等。function 必須接受兩個位置引數和第三個 msg=None 關鍵字引數,就像assertEqual()
一樣。當檢測到前兩個引數之間不相等時,它必須引發self.failureException(msg)
——可能會提供有用的資訊並在錯誤訊息中詳細解釋不相等之處。在 3.1 版本加入。
下表總結了
assertEqual()
自動使用的型別特定方法列表。請注意,通常不需要直接呼叫這些方法。方法
用於比較
新增於
字串
3.1
序列
3.1
列表
3.1
元組
3.1
集合或凍結集合
3.1
字典
3.1
- assertMultiLineEqual(first, second, msg=None)¶
測試多行字串 first 是否等於字串 second。如果不相等,錯誤訊息中將包含突出顯示差異的兩個字串的差異。此方法在預設情況下用於比較與
assertEqual()
的字串。在 3.1 版本加入。
- assertSequenceEqual(first, second, msg=None, seq_type=None)¶
測試兩個序列是否相等。如果提供了 seq_type,則 first 和 second 都必須是 seq_type 的例項,否則將引發失敗。如果序列不同,則會構造一條錯誤訊息,顯示兩者之間的差異。
此方法不直接由
assertEqual()
呼叫,但它用於實現assertListEqual()
和assertTupleEqual()
。在 3.1 版本加入。
- assertListEqual(first, second, msg=None)¶
- assertTupleEqual(first, second, msg=None)¶
測試兩個列表或元組是否相等。如果不相等,則會構造一條錯誤訊息,僅顯示兩者之間的差異。如果任一引數型別不正確,也會引發錯誤。這些方法在預設情況下用於比較與
assertEqual()
的列表或元組。在 3.1 版本加入。
- assertSetEqual(first, second, msg=None)¶
測試兩個集合是否相等。如果不相等,將構造一條錯誤訊息,列出集合之間的差異。此方法在預設情況下用於比較與
assertEqual()
的集合或凍結集合。如果 first 或 second 都沒有
set.difference()
方法,則失敗。在 3.1 版本加入。
- assertDictEqual(first, second, msg=None)¶
測試兩個字典是否相等。如果不相等,將構造一條錯誤訊息,顯示字典中的差異。此方法將預設用於在呼叫
assertEqual()
時比較字典。在 3.1 版本加入。
最後,
TestCase
提供了以下方法和屬性- fail(msg=None)¶
無條件地發出測試失敗訊號,錯誤訊息為 msg 或
None
。
- failureException¶
此類屬性提供測試方法引發的異常。如果測試框架需要使用專門的異常(可能用於攜帶附加資訊),則必須為此異常建立子類,以便與框架“公平地”互動。此屬性的初始值為
AssertionError
。
- longMessage¶
此類屬性決定當自定義失敗訊息作為 msg 引數傳遞給失敗的 assertXYY 呼叫時會發生什麼。
True
是預設值。在這種情況下,自定義訊息將附加到標準失敗訊息的末尾。當設定為False
時,自定義訊息將替換標準訊息。在呼叫 assert 方法之前,可以透過將例項屬性 self.longMessage 分配給
True
或False
來在各個測試方法中覆蓋類設定。類設定在每次測試呼叫之前都會重置。
在 3.1 版本加入。
- maxDiff¶
此屬性控制在失敗時報告差異的斷言方法輸出的差異的最大長度。它預設為 80*8 個字元。受此屬性影響的斷言方法是
assertSequenceEqual()
(包括所有委託給它的序列比較方法)、assertDictEqual()
和assertMultiLineEqual()
。將
maxDiff
設定為None
意味著差異沒有最大長度。在 3.2 版本加入。
測試框架可以使用以下方法收集測試資訊
- defaultTestResult()¶
返回一個測試結果類例項,該例項應在此測試用例類中使用(如果沒有向
run()
方法提供其他結果例項)。對於
TestCase
例項,這將始終是TestResult
的例項;TestCase
的子類應根據需要覆蓋此方法。
- id()¶
返回標識特定測試用例的字串。這通常是測試方法的完整名稱,包括模組和類名。
- shortDescription()¶
返回測試的描述,如果沒有提供描述則返回
None
。此方法的預設實現返回測試方法的文件字串的第一行(如果可用),否則返回None
。3.1 版中已更改: 在 3.1 版中,此功能進行了更改,即使存在文件字串,也會將測試名稱新增到簡短描述中。這導致了與 unittest 擴充套件的相容性問題,因此在 Python 3.2 中將測試名稱新增到
TextTestResult
中。
- addCleanup(function, /, *args, **kwargs)¶
新增一個函式,該函式將在
tearDown()
之後呼叫,以清理測試期間使用的資源。函式將以與新增順序相反的順序呼叫(後進先出)。它們將使用在新增時傳遞給addCleanup()
的所有引數和關鍵字引數呼叫。如果
setUp()
失敗,這意味著tearDown()
未被呼叫,那麼新增的任何清理函式仍將被呼叫。在 3.1 版本加入。
- enterContext(cm)¶
進入提供的 上下文管理器。如果成功,還會將其
__exit__()
方法作為清理函式透過addCleanup()
新增,並返回__enter__()
方法的結果。在 3.11 版本中新增。
- doCleanups()¶
此方法在
tearDown()
之後無條件呼叫,或者在setUp()
丟擲異常後呼叫。它負責呼叫透過
addCleanup()
新增的所有清理函式。如果你需要在tearDown()
之前呼叫清理函式,那麼你可以自行呼叫doCleanups()
。doCleanups()
逐個從清理函式堆疊中彈出方法,因此可以隨時呼叫。在 3.1 版本加入。
- classmethod addClassCleanup(function, /, *args, **kwargs)¶
新增一個函式,該函式將在
tearDownClass()
之後呼叫,以清理測試類期間使用的資源。函式將以與新增順序相反的順序呼叫(後進先出)。它們將使用在新增時傳遞給addClassCleanup()
的所有引數和關鍵字引數呼叫。如果
setUpClass()
失敗,這意味著tearDownClass()
未被呼叫,那麼新增的任何清理函式仍將被呼叫。在 3.8 版本加入。
- classmethod enterClassContext(cm)¶
進入提供的 上下文管理器。如果成功,還會將其
__exit__()
方法作為清理函式透過addClassCleanup()
新增,並返回__enter__()
方法的結果。在 3.11 版本中新增。
- classmethod doClassCleanups()¶
此方法在
tearDownClass()
之後無條件呼叫,或者在setUpClass()
丟擲異常後呼叫。它負責呼叫透過
addClassCleanup()
新增的所有清理函式。如果你需要在tearDownClass()
之前呼叫清理函式,那麼你可以自行呼叫doClassCleanups()
。doClassCleanups()
逐個從清理函式堆疊中彈出方法,因此可以隨時呼叫。在 3.8 版本加入。
- class unittest.IsolatedAsyncioTestCase(methodName='runTest')¶
此類的 API 類似於
TestCase
,也接受協程作為測試函式。在 3.8 版本加入。
- loop_factory¶
傳遞給
asyncio.Runner
的 loop_factory。在子類中用asyncio.EventLoop
覆蓋,以避免使用 asyncio 策略系統。在 3.13 版本加入。
- async asyncSetUp()¶
準備測試夾具的方法。此方法在
setUp()
之後呼叫。它在呼叫測試方法之前立即呼叫;除了AssertionError
或SkipTest
之外,此方法引發的任何異常都將被視為錯誤而不是測試失敗。預設實現不執行任何操作。
- async asyncTearDown()¶
在測試方法呼叫並記錄結果後立即呼叫的方法。此方法在
tearDown()
之前呼叫。即使測試方法引發了異常,此方法也會被呼叫,因此子類中的實現可能需要特別小心地檢查內部狀態。除了AssertionError
或SkipTest
之外,此方法引發的任何異常都將被視為額外的錯誤而不是測試失敗(從而增加報告錯誤的 RethinkDB)。無論測試方法的結果如何,只有當asyncSetUp()
成功時,此方法才會被呼叫。預設實現不執行任何操作。
- addAsyncCleanup(function, /, *args, **kwargs)¶
此方法接受一個協程,可以用作清理函式。
- async enterAsyncContext(cm)¶
進入提供的 非同步上下文管理器。如果成功,還會將其
__aexit__()
方法作為清理函式透過addAsyncCleanup()
新增,並返回__aenter__()
方法的結果。在 3.11 版本中新增。
- run(result=None)¶
設定一個新的事件迴圈來執行測試,將結果收集到作為 result 傳遞的
TestResult
物件中。如果省略 result 或為None
,則建立一個臨時結果物件(透過呼叫defaultTestResult()
方法)並使用。結果物件返回給run()
的呼叫者。在測試結束時,事件迴圈中的所有任務都將被取消。
一個說明順序的例子
from unittest import IsolatedAsyncioTestCase events = [] class Test(IsolatedAsyncioTestCase): def setUp(self): events.append("setUp") async def asyncSetUp(self): self._async_connection = await AsyncConnection() events.append("asyncSetUp") async def test_response(self): events.append("test_response") response = await self._async_connection.get("https://example.com") self.assertEqual(response.status_code, 200) self.addAsyncCleanup(self.on_cleanup) def tearDown(self): events.append("tearDown") async def asyncTearDown(self): await self._async_connection.close() events.append("asyncTearDown") async def on_cleanup(self): events.append("cleanup") if __name__ == "__main__": unittest.main()
執行測試後,
events
將包含["setUp", "asyncSetUp", "test_response", "asyncTearDown", "tearDown", "cleanup"]
。
分組測試¶
- class unittest.TestSuite(tests=())¶
此類別表示單個測試用例和測試套件的聚合。該類別提供了測試執行器所需的介面,允許它像任何其他測試用例一樣執行。
TestSuite
例項的執行與遍歷套件,單獨執行每個測試相同。如果提供了 tests,它必須是單個測試用例或其他測試套件的可迭代物件,用於初始構建套件。提供了其他方法來稍後向集合新增測試用例和套件。
TestSuite
物件行為與TestCase
物件非常相似,只是它們實際上不實現測試。相反,它們用於將測試聚合到應該一起執行的測試組中。一些額外的方法可用於向TestSuite
例項新增測試- run(result)¶
執行與此套件關聯的測試,將結果收集到作為 result 傳遞的測試結果物件中。請注意,與
TestCase.run()
不同,TestSuite.run()
要求傳入結果物件。
- debug()¶
執行與此套件關聯的測試而不收集結果。這允許測試引發的異常傳播到呼叫者,並可用於支援在偵錯程式下執行測試。
- countTestCases()¶
返回此測試物件表示的測試數量,包括所有單個測試和子套件。
- __iter__()¶
透過
TestSuite
分組的測試始終透過迭代訪問。子類可以透過覆蓋__iter__()
來延遲提供測試。請注意,此方法可以在單個套件上多次呼叫(例如在計數測試或比較相等性時),因此在TestSuite.run()
之前重複迭代返回的測試對於每次呼叫迭代必須相同。在TestSuite.run()
之後,除非呼叫者使用覆蓋TestSuite._removeTestAtIndex()
以保留測試引用的子類,否則呼叫者不應依賴此方法返回的測試。3.2 版中已更改: 在早期版本中,
TestSuite
直接訪問測試而不是透過迭代,因此覆蓋__iter__()
不足以提供測試。3.4 版中已更改: 在早期版本中,
TestSuite
在TestSuite.run()
之後持有對每個TestCase
的引用。子類可以透過覆蓋TestSuite._removeTestAtIndex()
來恢復該行為。
在
TestSuite
物件的典型用法中,run()
方法由TestRunner
呼叫,而不是由終端使用者測試工具呼叫。
載入和執行測試¶
- class unittest.TestLoader¶
TestLoader
類用於從類和模組建立測試套件。通常,無需建立此類的例項;unittest
模組提供了一個例項,可以作為unittest.defaultTestLoader
共享。但是,使用子類或例項允許自定義一些可配置的屬性。TestLoader
物件具有以下屬性- errors¶
載入測試時遇到的非致命錯誤列表。載入器在任何時候都不會重置。致命錯誤透過相關方法向呼叫者引發異常來發出訊號。非致命錯誤也透過一個合成測試來指示,該測試在執行時將引發原始錯誤。
在 3.5 版本加入。
TestLoader
物件具有以下方法- loadTestsFromTestCase(testCaseClass)¶
返回包含在
TestCase
派生類testCaseClass
中的所有測試用例的套件。為
getTestCaseNames()
命名的方法建立測試用例例項。預設情況下,這些方法名以test
開頭。如果getTestCaseNames()
不返回任何方法,但實現了runTest()
方法,則改為為該方法建立一個測試用例。
- loadTestsFromModule(module, *, pattern=None)¶
返回給定模組中包含的所有測試用例的套件。此方法在 module 中搜索派生自
TestCase
的類,併為該類定義的每個測試方法建立一個類例項。備註
雖然使用
TestCase
派生類的層次結構可以方便地共享 fixture 和輔助函式,但在不打算直接例項化基類上定義測試方法與此方法不太相容。然而,當 fixture 不同且在子類中定義時,這樣做可能很有用。如果模組提供了
load_tests
函式,則將呼叫它來載入測試。這允許模組自定義測試載入。這是 load_tests 協議。pattern 引數作為第三個引數傳遞給load_tests
。3.2 版中已更改: 增加了對
load_tests
的支援。3.5 版中已更改: 增加了對僅限關鍵字引數 pattern 的支援。
3.12 版中已更改: 未記錄且非官方的 use_load_tests 引數已移除。
- loadTestsFromName(name, module=None)¶
給定一個字串指定符,返回所有測試用例的套件。
指定符 name 是一個“點分名稱”,可以解析為模組、測試用例類、測試用例類中的測試方法、
TestSuite
例項,或返回TestCase
或TestSuite
例項的可呼叫物件。這些檢查按此處列出的順序應用;也就是說,可能測試用例類中的方法將被識別為“測試用例類中的測試方法”,而不是“可呼叫物件”。例如,如果您有一個模組
SampleTests
包含一個TestCase
派生類SampleTestCase
,其中包含三個測試方法(test_one()
、test_two()
和test_three()
),則指定符'SampleTests.SampleTestCase'
將導致此方法返回一個執行所有三個測試方法的套件。使用指定符'SampleTests.SampleTestCase.test_two'
將導致它返回一個僅執行test_two()
測試方法的測試套件。指定符可以引用尚未匯入的模組和包;它們將作為副作用匯入。該方法可以選擇性地相對於給定的 module 解析 name。
3.5 版中已更改: 如果在遍歷 name 時發生
ImportError
或AttributeError
,則將返回一個合成測試,該測試在執行時會引發該錯誤。這些錯誤包含在 self.errors 累積的錯誤中。
- loadTestsFromNames(names, module=None)¶
與
loadTestsFromName()
類似,但接受一個名稱序列而不是單個名稱。返回值是一個測試套件,支援為每個名稱定義的所有測試。
- discover(start_dir, pattern='test*.py', top_level_dir=None)¶
透過從指定的起始目錄遞迴到子目錄來查詢所有測試模組,並返回包含它們的 TestSuite 物件。只有與 pattern 匹配的測試檔案才會被載入。(使用 shell 風格的模式匹配。)只有可匯入的模組名稱(即有效的 Python 識別符號)才會被載入。
所有測試模組必須可以從專案的頂層匯入。如果起始目錄不是頂層目錄,則必須單獨指定 top_level_dir。
如果匯入模組失敗,例如由於語法錯誤,則這將記錄為單個錯誤,並且發現將繼續。如果匯入失敗是由於引發了
SkipTest
,則將記錄為跳過而不是錯誤。如果找到一個包(包含名為
__init__.py
的檔案的目錄),將檢查該包是否存在load_tests
函式。如果存在,則將呼叫package.load_tests(loader, tests, pattern)
。測試發現會確保在一次呼叫期間只檢查一個包的測試一次,即使 load_tests 函式本身呼叫了loader.discover
。如果存在
load_tests
,則發現 不會 遞迴到包中,load_tests
負責載入包中的所有測試。模式不會故意儲存為載入器屬性,以便包可以繼續自行發現。
top_level_dir 在內部儲存,並用作任何巢狀的
discover()
呼叫的預設值。也就是說,如果包的load_tests
呼叫loader.discover()
,則無需傳遞此引數。start_dir 可以是點分模組名稱,也可以是目錄。
在 3.2 版本加入。
3.4 版中已更改: 在匯入時引發
SkipTest
的模組被記錄為跳過,而不是錯誤。start_dir 可以是一個 名稱空間包。
路徑在匯入之前進行排序,以便即使底層檔案系統的排序不依賴於檔名,執行順序也相同。
3.5 版中已更改: 現在檢查詢到的包是否存在
load_tests
,無論其路徑是否與 pattern 匹配,因為包名稱不可能與預設模式匹配。版本 3.11 中的變化: start_dir 不能是名稱空間包。自 Python 3.7 以來,它已損壞,Python 3.11 正式移除了它。
版本 3.13 中的變化: top_level_dir 僅在 discover 呼叫期間儲存。
版本 3.14 中的變化: start_dir 再次可以是一個名稱空間包。
TestLoader
的以下屬性可以透過子類化或例項賦值進行配置- testMethodPrefix¶
字串,表示將被解釋為測試方法的函式名字首。預設值為
'test'
。這會影響
getTestCaseNames()
和所有loadTestsFrom*
方法。
- sortTestMethodsUsing¶
在
getTestCaseNames()
和所有loadTestsFrom*
方法中對方法名進行排序時使用的比較函式。
- testNamePatterns¶
Unix shell 風格的萬用字元測試名稱模式列表,測試方法必須匹配這些模式才能包含在測試套件中(參見
-k
選項)。如果此屬性不為
None
(預設值),則所有要包含在測試套件中的測試方法都必須與此列表中的某個模式匹配。請注意,匹配總是使用fnmatch.fnmatchcase()
執行,因此與傳遞給-k
選項的模式不同,簡單的子字串模式必須使用*
萬用字元進行轉換。這會影響所有
loadTestsFrom*
方法。在 3.7 版本加入。
- class unittest.TestResult¶
這個類用於編譯有關哪些測試成功哪些測試失敗的資訊。
一個
TestResult
物件儲存一組測試的結果。TestCase
和TestSuite
類確保結果被正確記錄;測試作者不需要擔心記錄測試結果。基於
unittest
構建的測試框架可能希望訪問執行一組測試生成的TestResult
物件以進行報告;TestRunner.run()
方法為此目的返回一個TestResult
例項。TestResult
例項具有以下屬性,這些屬性在檢查一組測試執行結果時會很有用- collectedDurations¶
一個包含測試用例名稱和浮點數(表示每個已執行測試的耗時)的 2 元組列表。
3.12 新版功能.
- testsRun¶
到目前為止執行的測試總數。
- buffer¶
如果設定為 true,
sys.stdout
和sys.stderr
將在呼叫startTest()
和stopTest()
之間進行緩衝。收集到的輸出只有在測試失敗或出錯時才會回顯到真實的sys.stdout
和sys.stderr
。任何輸出也會附加到失敗/錯誤訊息中。在 3.2 版本加入。
- tb_locals¶
如果設定為 true,則本地變數將顯示在回溯中。
在 3.5 版本加入。
- wasSuccessful()¶
如果到目前為止所有測試都透過,則返回
True
,否則返回False
。版本 3.4 中的變化: 如果存在任何由
expectedFailure()
裝飾器標記的測試中的unexpectedSuccesses
,則返回False
。
- stop()¶
此方法可用於發出訊號,透過將
shouldStop
屬性設定為True
,應中止正在執行的測試集。TestRunner
物件應遵守此標誌並返回而不執行任何其他測試。例如,此功能被
TextTestRunner
類用於在使用者從鍵盤發出中斷訊號時停止測試框架。提供TestRunner
實現的互動式工具可以以類似的方式使用此功能。
TestResult
類的以下方法用於維護內部資料結構,並可在子類中擴充套件以支援其他報告要求。這在構建支援互動式報告的工具時特別有用,而測試正在執行。- startTest(test)¶
當測試用例 test 即將執行時呼叫。
- stopTest(test)¶
在測試用例 test 執行後呼叫,無論結果如何。
- startTestRun()¶
在任何測試執行之前呼叫一次。
在 3.1 版本加入。
- stopTestRun()¶
在所有測試執行後呼叫一次。
在 3.1 版本加入。
- addError(test, err)¶
當測試用例 test 引發意外異常時呼叫。err 是一個元組,其形式由
sys.exc_info()
返回:(type, value, traceback)
。預設實現將元組
(test, formatted_err)
附加到例項的errors
屬性中,其中 formatted_err 是從 err 派生出的格式化回溯。
- addFailure(test, err)¶
當測試用例 test 發出失敗訊號時呼叫。err 是一個元組,其形式由
sys.exc_info()
返回:(type, value, traceback)
。預設實現將元組
(test, formatted_err)
附加到例項的failures
屬性中,其中 formatted_err 是從 err 派生出的格式化回溯。
- addSuccess(test)¶
當測試用例 test 成功時呼叫。
預設實現不執行任何操作。
- addSkip(test, reason)¶
當測試用例 test 被跳過時呼叫。reason 是測試給出的跳過原因。
預設實現將元組
(test, reason)
附加到例項的skipped
屬性中。
- addExpectedFailure(test, err)¶
當測試用例 test 失敗或出錯,但被
expectedFailure()
裝飾器標記時呼叫。預設實現將元組
(test, formatted_err)
附加到例項的expectedFailures
屬性中,其中 formatted_err 是從 err 派生出的格式化回溯。
- addUnexpectedSuccess(test)¶
當測試用例 test 被
expectedFailure()
裝飾器標記,但卻成功時呼叫。預設實現將測試附加到例項的
unexpectedSuccesses
屬性中。
- addSubTest(test, subtest, outcome)¶
當一個子測試完成時呼叫。test 是與測試方法對應的測試用例。subtest 是描述子測試的自定義
TestCase
例項。如果 outcome 為
None
,則子測試成功。否則,它會因異常而失敗,其中 outcome 是一個元組,其形式由sys.exc_info()
返回:(type, value, traceback)
。預設實現對成功的結果不執行任何操作,並將子測試失敗記錄為正常失敗。
在 3.4 版本加入。
- addDuration(test, elapsed)¶
當測試用例完成時呼叫。elapsed 是以秒錶示的時間,它包括清理函式的執行。
3.12 新版功能.
- class unittest.TextTestResult(stream, descriptions, verbosity, *, durations=None)¶
TestResult
的具體實現,由TextTestRunner
使用。子類應接受**kwargs
以確保介面更改時的相容性。在 3.2 版本加入。
版本 3.12 中的變化: 添加了 durations 關鍵字引數。
- unittest.defaultTestLoader¶
TestLoader
類的例項,旨在共享。如果不需要自定義TestLoader
,則可以使用此例項,而無需重複建立新例項。
- class unittest.TextTestRunner(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False, durations=None)¶
一個基本的測試執行器實現,將結果輸出到流。如果 stream 為
None
(預設值),則sys.stderr
用作輸出流。此類具有一些可配置引數,但本質上非常簡單。執行測試套件的圖形應用程式應提供替代實現。此類實現應接受**kwargs
,因為當向 unittest 新增功能時,構建執行器的介面會發生變化。預設情況下,此執行器會顯示
DeprecationWarning
、PendingDeprecationWarning
、ResourceWarning
和ImportWarning
,即使它們預設被忽略。此行為可以透過使用 Python 的-Wd
或-Wa
選項(參見警告控制)並保留 warnings 為None
來覆蓋。版本 3.2 中的變化: 添加了 warnings 引數。
版本 3.2 中的變化: 預設流在例項化時而不是匯入時設定為
sys.stderr
。版本 3.5 中的變化: 添加了 tb_locals 引數。
版本 3.12 中的變化: 添加了 durations 引數。
- _makeResult()¶
此方法返回由
run()
使用的TestResult
例項。它不應直接呼叫,但可以在子類中重寫以提供自定義TestResult
。_makeResult()
例項化傳遞給TextTestRunner
建構函式作為resultclass
引數的類或可呼叫物件。如果未提供resultclass
,則預設為TextTestResult
。結果類使用以下引數例項化stream, descriptions, verbosity
- run(test)¶
此方法是
TextTestRunner
的主要公共介面。此方法接受TestSuite
或TestCase
例項。透過呼叫_makeResult()
建立TestResult
,並執行測試並將結果列印到標準輸出。
- unittest.main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)¶
一個命令列程式,從 module 載入一組測試並執行它們;這主要用於方便地使測試模組可執行。此函式最簡單的用法是在測試指令碼末尾包含以下行
if __name__ == '__main__': unittest.main()
您可以透過傳入 verbosity 引數來執行具有更詳細資訊的測試
if __name__ == '__main__': unittest.main(verbosity=2)
defaultTest 引數是單個測試的名稱或測試名稱的可迭代物件,用於在未透過 argv 指定測試名稱時執行。如果未指定或為
None
且未透過 argv 提供測試名稱,則執行 module 中找到的所有測試。argv 引數可以是傳遞給程式的選項列表,其中第一個元素是程式名稱。如果未指定或為
None
,則使用sys.argv
的值。testRunner 引數可以是測試執行器類或其已建立的例項。預設情況下,
main
呼叫sys.exit()
,並帶有一個表示測試執行成功 (0) 或失敗 (1) 的退出程式碼。退出程式碼 5 表示沒有執行或跳過任何測試。testLoader 引數必須是
TestLoader
例項,預設為defaultTestLoader
。main
支援透過傳入引數exit=False
從互動式直譯器使用。這將在標準輸出上顯示結果,而無需呼叫sys.exit()
>>> from unittest import main >>> main(module='test_module', exit=False)
failfast 、 catchbreak 和 buffer 引數與同名的 命令列選項 具有相同的效果。
warnings 引數指定在執行測試時應使用的警告過濾器。如果未指定,如果 python 傳遞了
-W
選項(請參閱警告控制),則它將保持None
,否則它將設定為'default'
。呼叫
main
會返回一個物件,其result
屬性包含作為unittest.TestResult
執行的測試結果。版本 3.1 中的變化: 添加了 exit 引數。
版本 3.2 中的變化: 添加了 verbosity、failfast、catchbreak、buffer 和 warnings 引數。
版本 3.4 中的變化: defaultTest 引數已更改為也接受測試名稱的可迭代物件。
load_tests 協議¶
在 3.2 版本加入。
模組或包可以透過實現一個名為 load_tests
的函式來定製在正常測試執行或測試發現期間如何從它們載入測試。
如果一個測試模組定義了 load_tests
,它將被 TestLoader.loadTestsFromModule()
呼叫,並帶以下引數
load_tests(loader, standard_tests, pattern)
其中 pattern 直接從 loadTestsFromModule
傳遞。它預設為 None
。
它應該返回一個 TestSuite
。
loader 是執行載入的 TestLoader
例項。standard_tests 是預設從模組載入的測試。測試模組通常只希望從標準測試集中新增或刪除測試。第三個引數在作為測試發現的一部分載入包時使用。
一個典型的 load_tests
函式,它從一組特定的 TestCase
類載入測試,可能看起來像
test_cases = (TestCase1, TestCase2, TestCase3)
def load_tests(loader, tests, pattern):
suite = TestSuite()
for test_class in test_cases:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
return suite
如果在包含包的目錄中啟動發現,無論是從命令列還是透過呼叫 TestLoader.discover()
,則會檢查包的 __init__.py
是否存在 load_tests
。如果該函式不存在,則發現將遞迴到包中,就像它只是另一個目錄一樣。否則,包的測試發現將留給 load_tests
,該函式將使用以下引數呼叫
load_tests(loader, standard_tests, pattern)
這應該返回一個代表包中所有測試的 TestSuite
。(standard_tests
將只包含從 __init__.py
收集的測試。)
因為模式被傳遞給 load_tests
,所以包可以自由地繼續(並可能修改)測試發現。一個“什麼都不做”的測試包的 load_tests
函式會是這樣的
def load_tests(loader, standard_tests, pattern):
# top level directory cached on loader instance
this_dir = os.path.dirname(__file__)
package_tests = loader.discover(start_dir=this_dir, pattern=pattern)
standard_tests.addTests(package_tests)
return standard_tests
版本 3.5 中的變化: 由於包名不可能匹配預設模式,發現不再檢查包名是否匹配 pattern。
類和模組夾具¶
類和模組級別的夾具在 TestSuite
中實現。當測試套件遇到來自新類的測試時,將呼叫前一個類(如果存在)的 tearDownClass()
,然後呼叫新類的 setUpClass()
。
類似地,如果測試來自與前一個測試不同的模組,則會執行前一個模組的 tearDownModule
,然後執行新模組的 setUpModule
。
所有測試執行後,會執行最終的 tearDownClass
和 tearDownModule
。
請注意,共享夾具與(潛在的)測試並行化等功能配合不佳,它們會破壞測試隔離。應謹慎使用它們。
unittest 測試載入器建立的測試預設順序是將同一模組和類中的所有測試分組在一起。這將導致 setUpClass
/ setUpModule
(等)每個類和模組只被呼叫一次。如果您隨機化順序,使來自不同模組和類的測試彼此相鄰,則這些共享夾具函式可能會在一次測試執行中被多次呼叫。
共享夾具不適用於非標準排序的套件。BaseTestSuite
仍然存在,供不想支援共享夾具的框架使用。
如果在其中一個共享夾具函式期間引發任何異常,則該測試將報告為錯誤。由於沒有相應的測試例項,因此會建立一個 _ErrorHolder
物件(具有與 TestCase
相同的介面)來表示錯誤。如果您只是使用標準的 unittest 測試執行器,則此細節無關緊要,但如果您是框架作者,則可能相關。
setUpClass 和 tearDownClass¶
這些必須作為類方法實現
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._connection = createExpensiveConnectionObject()
@classmethod
def tearDownClass(cls):
cls._connection.destroy()
如果您希望呼叫基類上的 setUpClass
和 tearDownClass
,則必須自己向上呼叫它們。TestCase
中的實現為空。
如果在 setUpClass
期間引發異常,則類中的測試將不執行,並且 tearDownClass
將不執行。跳過的類將不執行 setUpClass
或 tearDownClass
。如果異常是 SkipTest
異常,則該類將被報告為已跳過,而不是錯誤。
setUpModule 和 tearDownModule¶
這些應該作為函式實現
def setUpModule():
createConnection()
def tearDownModule():
closeConnection()
如果在 setUpModule
中引發異常,則模組中的所有測試都不會執行,並且 tearDownModule
也不會執行。如果異常是 SkipTest
異常,則模組將被報告為已跳過,而不是錯誤。
要新增即使在異常情況下也必須執行的清理程式碼,請使用 addModuleCleanup
- unittest.addModuleCleanup(function, /, *args, **kwargs)¶
新增一個函式,該函式將在
tearDownModule()
之後呼叫,以清理測試類期間使用的資源。函式將以與新增它們的順序相反的順序呼叫(後進先出)。它們將使用在新增它們時傳遞給addModuleCleanup()
的任何引數和關鍵字引數進行呼叫。如果
setUpModule()
失敗,這意味著tearDownModule()
未被呼叫,那麼新增的任何清理函式仍將被呼叫。在 3.8 版本加入。
- unittest.enterModuleContext(cm)¶
進入提供的上下文管理器。如果成功,還將其
__exit__()
方法作為清理函式透過addModuleCleanup()
新增,並返回__enter__()
方法的結果。在 3.11 版本中新增。
- unittest.doModuleCleanups()¶
此函式在
tearDownModule()
之後無條件呼叫,或者在setUpModule()
之後(如果setUpModule()
引發異常)。它負責呼叫
addModuleCleanup()
新增的所有清理函式。如果您需要在tearDownModule()
之前 呼叫清理函式,則可以自己呼叫doModuleCleanups()
。doModuleCleanups()
一次從清理函式堆疊中彈出一個方法,因此可以隨時呼叫它。在 3.8 版本加入。
訊號處理¶
在 3.2 版本加入。
unittest 的 -c/--catch
命令列選項,以及 unittest.main()
的 catchbreak
引數,提供了在測試執行期間更友好的 Control-C 處理。啟用中斷捕獲行為後,Control-C 將允許當前正在執行的測試完成,然後測試執行將結束並報告所有已獲得的結果。第二次 Control-C 將以通常的方式引發 KeyboardInterrupt
。
Control-C 處理訊號處理器試圖與安裝自己的 signal.SIGINT
處理程式的程式碼或測試保持相容。如果呼叫了 unittest
處理程式但它 不是 已安裝的 signal.SIGINT
處理程式,即它已被被測系統替換並委託給它,那麼它將呼叫預設處理程式。這通常是替換已安裝處理程式並委託給它的程式碼的預期行為。對於需要停用 unittest
Control-C 處理的單個測試,可以使用 removeHandler()
裝飾器。
有一些實用函式可供框架作者在測試框架中啟用 Control-C 處理功能。
- unittest.installHandler()¶
安裝 Control-C 處理程式。當收到
signal.SIGINT
(通常是使用者按下 Control-C 的響應)時,所有已註冊的結果都將呼叫stop()
。
- unittest.registerResult(result)¶
註冊一個用於 Control-C 處理的
TestResult
物件。註冊結果會儲存一個弱引用,因此它不會阻止結果被垃圾回收。如果 Control-C 處理未啟用,註冊
TestResult
物件沒有副作用,因此測試框架可以無條件地註冊它們建立的所有結果,而不管處理是否啟用。
- unittest.removeHandler(function=None)¶
在不帶引數呼叫時,此函式將刪除 Control-C 處理程式(如果已安裝)。此函式還可以用作測試裝飾器,以在測試執行期間臨時刪除處理程式
@unittest.removeHandler def test_signal_handling(self): ...