unittest
— 單元測試框架¶
(如果您已經熟悉測試的基本概念,您可能想跳到斷言方法列表。)
unittest
單元測試框架最初受 JUnit 的啟發,並且與其他語言中的主要單元測試框架具有相似的風味。它支援測試自動化、共享測試的設定和關閉程式碼、將測試聚合到集合中,以及測試與報告框架的獨立性。
為了實現這一點,unittest
以面向物件的方式支援一些重要概念
- 測試裝置
測試裝置 表示執行一個或多個測試所需的準備工作,以及任何相關的清理操作。例如,這可能涉及建立臨時或代理資料庫、目錄,或啟動伺服器程序。
- 測試用例
測試用例 是測試的單個單元。它檢查對特定輸入集的特定響應。
unittest
提供了一個基類TestCase
,可用於建立新的測試用例。- 測試套件
測試套件 是測試用例、測試套件或兩者的集合。它用於聚合應該一起執行的測試。
- 測試執行器
測試執行器 是一個協調測試執行並將結果提供給使用者的元件。執行器可以使用圖形介面、文字介面,或返回一個特殊值來指示執行測試的結果。
參見
- 模組
doctest
另一個具有非常不同風格的測試支援模組。
- 簡單的 Smalltalk 測試:使用模式
Kent Beck 關於使用
unittest
共享的模式的測試框架的原始論文。- pytest
第三方單元測試框架,具有更輕量級的編寫測試語法。例如,
assert func(10) == 42
。- Python 測試工具分類
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 版本中變更: 在早期版本中,只能執行單個測試方法,而不能執行模組或類。
命令列選項¶
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 版本中變更: 測試發現支援起始目錄的名稱空間包。請注意,您還需要指定頂層目錄(例如 python -m unittest discover -s root/namespace -t root
)。
在 3.11 版本中變更: unittest
在 Python 3.11 中放棄了對名稱空間包的支援。自 Python 3.7 以來,它一直處於損壞狀態。包含測試的起始目錄和子目錄必須是具有 __init__.py
檔案的常規包。
包含起始目錄的目錄仍然可以是名稱空間包。在這種情況下,您需要將起始目錄指定為點分隔的包名稱,並顯式指定目標目錄。例如
# proj/ <-- current directory
# namespace/
# mypkg/
# __init__.py
# test_mypkg.py
python -m unittest discover -s namespace.mypkg -t .
組織測試程式碼¶
單元測試的基本構建塊是測試用例 — 必須設定並檢查正確性的單個場景。在 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))
請注意,為了測試某些內容,我們使用 assert* 方法,這些方法由 TestCase
基類提供。如果測試失敗,將引發帶有解釋性訊息的異常,並且 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()
裝飾器 或其條件變體之一,呼叫 TestCase.skipTest()
在 setUp()
或測試方法中,或直接引發 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
所有斷言方法都接受一個 msg 引數,如果指定了該引數,則在失敗時用作錯誤訊息(另請參見
longMessage
)。請注意,msg 關鍵字引數只能在用作上下文管理器時傳遞給assertRaises()
,assertRaisesRegex()
,assertWarns()
,assertWarnsRegex()
。- assertEqual(first, second, msg=None)¶
測試 first 和 second 是否相等。如果值不相等,則測試將失敗。
此外,如果 first 和 second 是完全相同的型別,並且是 list、tuple、dict、set、frozenset 或 str 中的一種,或者任何子類使用
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 版本中新增。
還可以使用以下方法檢查異常、警告和日誌訊息的產生
方法
檢查
新增於
fun(*args, **kwds)
丟擲 excfun(*args, **kwds)
丟擲 exc 並且訊息與正則表示式 r 匹配3.1
fun(*args, **kwds)
丟擲 warn3.2
fun(*args, **kwds)
丟擲 warn 並且訊息與正則表示式 r 匹配3.2
with
程式碼塊在 logger 上記錄,最低級別為 level3.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
- 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 版本中新增。
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 擴充套件的相容性問題,並將測試名稱新增到
TextTestResult
(在 Python 3.2 中)。
- addCleanup(function, /, *args, **kwargs)¶
新增一個函式,在
tearDown()
之後呼叫,以清理測試期間使用的資源。函式將按照它們新增的相反順序(後進先出 LIFO)呼叫。呼叫時,會將傳遞給addCleanup()
的任何引數和關鍵字引數傳遞給它們。如果
setUp()
失敗,這意味著不會呼叫tearDown()
,那麼新增的任何清理函式仍將被呼叫。在 3.1 版本中新增。
- enterContext(cm)¶
進入提供的 上下文管理器。如果成功,還會透過
addCleanup()
將其__exit__()
方法新增為清理函式,並返回__enter__()
方法的結果。3.11 版本新增。
- doCleanups()¶
此方法在
tearDown()
之後或在setUp()
引發異常後無條件呼叫。它負責呼叫由
addCleanup()
新增的所有清理函式。如果您需要清理函式在tearDown()
之前 呼叫,那麼您可以自己呼叫doCleanups()
。doCleanups()
逐個彈出清理函式堆疊中的方法,因此可以隨時呼叫。在 3.1 版本中新增。
- classmethod addClassCleanup(function, /, *args, **kwargs)¶
新增一個函式,在
tearDownClass()
之後呼叫,以清理測試類期間使用的資源。函式將按照它們新增的相反順序(後進先出 LIFO)呼叫。呼叫時,會將傳遞給addClassCleanup()
的任何引數和關鍵字引數傳遞給它們。如果
setUpClass()
失敗,這意味著不會呼叫tearDownClass()
,那麼新增的任何清理函式仍將被呼叫。3.8 版本新增。
- classmethod enterClassContext(cm)¶
進入提供的 上下文管理器。如果成功,還會透過
addClassCleanup()
將其__exit__()
方法新增為清理函式,並返回__enter__()
方法的結果。3.11 版本新增。
- classmethod doClassCleanups()¶
此方法在
tearDownClass()
之後或在setUpClass()
引發異常後無條件呼叫。它負責呼叫所有由
addClassCleanup()
新增的清理函式。 如果您需要在tearDownClass()
之前 呼叫清理函式,那麼您可以自己呼叫doClassCleanups()
。doClassCleanups()
每次從清理函式堆疊中彈出一個方法,因此可以隨時呼叫它。3.8 版本新增。
- class unittest.IsolatedAsyncioTestCase(methodName='runTest')¶
此類提供類似於
TestCase
的 API,並且還接受協程作為測試函式。3.8 版本新增。
- loop_factory¶
傳遞給
asyncio.Runner
的 loop_factory。在子類中使用asyncio.EventLoop
覆蓋,以避免使用 asyncio 策略系統。在 3.13 版本中新增。
- coroutine asyncSetUp()¶
呼叫此方法來準備測試夾具。它在
setUp()
之後呼叫。它在呼叫測試方法之前立即呼叫;除了AssertionError
或SkipTest
之外,此方法引發的任何異常都將被視為錯誤而不是測試失敗。預設實現不執行任何操作。
- coroutine asyncTearDown()¶
在測試方法被呼叫且結果被記錄後立即呼叫的方法。它在
tearDown()
之前呼叫。即使測試方法引發了異常也會呼叫此方法,因此子類中的實現可能需要特別小心地檢查內部狀態。此方法引發的任何異常,除了AssertionError
或SkipTest
之外,都將被視為額外的錯誤,而不是測試失敗(從而增加報告的錯誤總數)。只有在asyncSetUp()
成功時才會呼叫此方法,無論測試方法的結果如何。預設實現不執行任何操作。
- addAsyncCleanup(function, /, *args, **kwargs)¶
此方法接受可用作清理函式的協程。
- coroutine enterAsyncContext(cm)¶
進入提供的非同步上下文管理器。如果成功,還會透過
addAsyncCleanup()
將其__aexit__()
方法新增為清理函式,並返回__aenter__()
方法的結果。3.11 版本新增。
- run(result=None)¶
設定一個新的事件迴圈來執行測試,將結果收集到作為 result 傳遞的
TestResult
物件中。如果省略 result 或 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()
來恢復該行為。
載入和執行測試¶
- 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
派生類的層次結構可以方便地共享 fixtures 和輔助函式,但在不打算直接例項化的基類上定義測試方法並不適用於此方法。但是,當 fixtures 不同且在子類中定義時,這樣做可能很有用。如果模組提供了
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()
測試方法的測試套件。 說明符可以引用尚未匯入的模組和包;它們將作為副作用匯入。該方法可以選擇相對於給定的模組解析名稱。
在 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
的模組會記錄為跳過,而不是錯誤。在 3.4 版本中變更: start_dir 可以是 名稱空間包。
在 3.4 版本中變更: 路徑在匯入之前進行排序,以便即使底層檔案系統的順序不依賴於檔名,執行順序也相同。
在 3.5 版本中變更: 現在,無論找到的包的路徑是否與 pattern 匹配,都會檢查這些包是否存在
load_tests
,因為包名稱不可能與預設模式匹配。在 3.11 版本中變更: start_dir 不能是 名稱空間包。自 Python 3.7 以來,它已被破壞,Python 3.11 正式將其刪除。
在 3.13 版本中變更: top_level_dir 僅在 discover 呼叫的持續時間記憶體儲。
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
例項具有以下屬性,這些屬性在檢查執行一組測試的結果時會很有用- failures¶
一個列表,包含
TestCase
例項和包含格式化回溯的字串的 2 元組。每個元組表示使用 assert* 方法 明確發出失敗訊號的測試。
- collectedDurations¶
一個列表,其中包含測試用例名稱和浮點數的 2 元組,表示每個執行的測試的經過時間。
在 3.12 版本中新增。
- testsRun¶
到目前為止執行的測試總數。
- buffer¶
如果設定為 true,則在
startTest()
和stopTest()
被呼叫之間,sys.stdout
和sys.stderr
將被緩衝。只有當測試失敗或出錯時,收集到的輸出才會回顯到真實的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 成功時呼叫。
預設實現不執行任何操作。
- 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)¶
TextTestRunner
使用的TestResult
的具體實現。子類應接受**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
作為輸出流。此類有一些可配置的引數,但本質上非常簡單。執行測試套件的圖形應用程式應提供替代實現。當 unittest 新增功能時,此類實現應接受**kwargs
作為構造執行器的介面。預設情況下,此執行器顯示
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
,並執行測試並將結果列印到 stdout。
- 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
。透過傳入引數
exit=False
,可以在互動式直譯器中使用main
。這會在標準輸出上顯示結果,而不會呼叫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 是預設情況下將從模組載入的測試。測試模組通常只希望從標準測試集中新增或刪除測試。第三個引數用於在測試發現中載入包。
一個典型的從一組特定的 TestCase
類載入測試的 load_tests
函式可能如下所示
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()
之後呼叫,以清理測試類期間使用的資源。 函式將按照新增順序的反向順序(LIFO)呼叫。當它們被新增時,會使用傳遞給addModuleCleanup()
的任何引數和關鍵字引數來呼叫它們。如果
setUpModule()
失敗,意味著不會呼叫tearDownModule()
,那麼任何新增的清理函式仍然會被呼叫。3.8 版本新增。
- classmethod unittest.enterModuleContext(cm)¶
進入提供的上下文管理器。如果成功,還會透過
addModuleCleanup()
將其__exit__()
方法新增為清理函式,並返回__enter__()
方法的結果。3.11 版本新增。
- unittest.doModuleCleanups()¶
此函式在
tearDownModule()
之後無條件呼叫,或者在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): ...