unittest — 單元測試框架

原始碼: Lib/unittest/__init__.py


(如果你已經熟悉測試的基本概念,你可能想跳到斷言方法列表。)

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 工具。這主要是為了方便新接觸單元測試的使用者。對於生產環境,建議由持續整合系統驅動測試,例如 BuildbotJenkinsGitHub ActionsAppVeyor

基本示例

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_somethingbar_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 為此提供了一個機制:測試套件,由 unittestTestSuite 類表示。在大多數情況下,呼叫 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() 方法。

3.2 版本發生變化: TestCase 可以不提供 *methodName* 成功例項化。這使得在互動式直譯器中試驗 TestCase 變得更容易。

TestCase 例項提供三組方法:一組用於執行測試,另一組用於測試實現以檢查條件和報告失敗,以及一些允許收集測試本身資訊的查詢方法。

第一組(執行測試)中的方法是

setUp()

用於準備測試夾具的方法。它在呼叫測試方法之前立即呼叫;除了 AssertionErrorSkipTest 之外,此方法引發的任何異常都將被視為錯誤而不是測試失敗。預設實現不做任何事情。

tearDown()

在測試方法呼叫並記錄結果後立即呼叫的方法。即使測試方法引發了異常,也會呼叫此方法,因此子類中的實現可能需要特別注意檢查內部狀態。此方法引發的任何異常(AssertionErrorSkipTest 除外)都將被視為額外錯誤而不是測試失敗(因此會增加報告錯誤的總體數量)。此方法僅在 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 不返回結果。呼叫例項也不返回結果。

skipTest(reason)

在測試方法或 setUp() 期間呼叫此方法會跳過當前測試。有關更多資訊,請參閱 跳過測試和預期失敗

在 3.1 版本加入。

subTest(msg=None, **params)

返回一個上下文管理器,它將執行包含的程式碼塊作為子測試。 *msg* 和 *params* 是可選的任意值,當子測試失敗時會顯示出來,讓您可以清晰地識別它們。

一個測試用例可以包含任意數量的子測試宣告,並且它們可以任意巢狀。

有關詳細資訊,請參閱 使用子測試區分測試迭代

在 3.4 版本加入。

debug()

執行測試而不收集結果。這允許測試引發的異常傳播給呼叫者,並且可以用於支援在偵錯程式下執行測試。

TestCase 類提供了幾種斷言方法來檢查和報告失敗。下表列出了最常用的方法(更多斷言方法請參見下表)

方法

檢查

新增於

assertEqual(a, b)

a == b

assertNotEqual(a, b)

a != b

assertTrue(x)

bool(x) is True

assertFalse(x)

bool(x) is False

assertIs(a, b)

a is b

3.1

assertIsNot(a, b)

a is not b

3.1

assertIsNone(x)

x is None

3.1

assertIsNotNone(x)

x is not None

3.1

assertIn(a, b)

a in b

3.1

assertNotIn(a, b)

a not in b

3.1

assertIsInstance(a, b)

isinstance(a, b)

3.2

assertNotIsInstance(a, b)

not isinstance(a, b)

3.2

assertIsSubclass(a, b)

issubclass(a, b)

3.14

assertNotIsSubclass(a, b)

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 版本加入。

assertIsNone(expr, msg=None)
assertIsNotNone(expr, msg=None)

測試 *expr* 是否為(或不為)None

在 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 版本加入。

還可以使用以下方法檢查異常、警告和日誌訊息的生成

方法

檢查

新增於

assertRaises(exc, fun, *args, **kwds)

fun(*args, **kwds) 引發 *exc*

assertRaisesRegex(exc, r, fun, *args, **kwds)

fun(*args, **kwds) 引發 *exc* 且訊息匹配正則表示式 *r*

3.1

assertWarns(warn, fun, *args, **kwds)

fun(*args, **kwds) 引發 *warn*

3.2

assertWarnsRegex(warn, r, fun, *args, **kwds)

fun(*args, **kwds) 引發 *warn* 且訊息匹配正則表示式 *r*

3.2

assertLogs(logger, level)

with 塊在 *logger* 上記錄,最小級別為 *level*

3.4

assertNoLogs(logger, level)

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 屬性中,並將觸發警告的源行儲存在 filenamelineno 屬性中。如果目的是對捕獲到的警告執行額外的檢查,這會很有用

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 物件列表。

output

匹配訊息的格式化輸出的 str 物件列表。

示例

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 版本加入。

還有其他方法用於執行更具體的檢查,例如

方法

檢查

新增於

assertAlmostEqual(a, b)

round(a-b, 7) == 0

assertNotAlmostEqual(a, b)

round(a-b, 7) != 0

assertGreater(a, b)

a > b

3.1

assertGreaterEqual(a, b)

a >= b

3.1

assertLess(a, b)

a < b

3.1

assertLessEqual(a, b)

a <= b

3.1

assertRegex(s, r)

r.search(s)

3.1

assertNotRegex(s, r)

not r.search(s)

3.2

assertCountEqual(a, b)

無論順序如何,ab 具有相同數量的相同元素。

3.2

assertStartsWith(a, b)

a.startswith(b)

3.14

assertNotStartsWith(a, b)

not a.startswith(b)

3.14

assertEndsWith(a, b)

a.endswith(b)

3.14

assertNotEndsWith(a, b)

not a.endswith(b)

3.14

assertHasAttr(a, b)

hastattr(a, b)

3.14

assertNotHasAttr(a, b)

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),並與零進行比較,測試 firstsecond 大致(或不大致)相等。請注意,這些方法將值四捨五入到給定數量的 小數位數(即像 round() 函式一樣),而不是 有效數字

如果提供了 delta 而不是 places,則 firstsecond 之間的差異必須小於或等於(或大於)delta

同時提供 deltaplaces 將引發 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 相同的元素,無論其順序如何。如果它們不相等,將生成一條錯誤訊息,列出序列之間的差異。

在比較 firstsecond 時,不會忽略重複元素。它會驗證每個元素在兩個序列中是否具有相同的計數。等同於: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() 自動使用的型別特定方法列表。請注意,通常不需要直接呼叫這些方法。

方法

用於比較

新增於

assertMultiLineEqual(a, b)

字串

3.1

assertSequenceEqual(a, b)

序列

3.1

assertListEqual(a, b)

列表

3.1

assertTupleEqual(a, b)

元組

3.1

assertSetEqual(a, b)

集合或凍結集合

3.1

assertDictEqual(a, b)

字典

3.1

assertMultiLineEqual(first, second, msg=None)

測試多行字串 first 是否等於字串 second。如果不相等,錯誤訊息中將包含突出顯示差異的兩個字串的差異。此方法在預設情況下用於比較與 assertEqual() 的字串。

在 3.1 版本加入。

assertSequenceEqual(first, second, msg=None, seq_type=None)

測試兩個序列是否相等。如果提供了 seq_type,則 firstsecond 都必須是 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() 的集合或凍結集合。

如果 firstsecond 都沒有 set.difference() 方法,則失敗。

在 3.1 版本加入。

assertDictEqual(first, second, msg=None)

測試兩個字典是否相等。如果不相等,將構造一條錯誤訊息,顯示字典中的差異。此方法將預設用於在呼叫 assertEqual() 時比較字典。

在 3.1 版本加入。

最後,TestCase 提供了以下方法和屬性

fail(msg=None)

無條件地發出測試失敗訊號,錯誤訊息為 msgNone

failureException

此類屬性提供測試方法引發的異常。如果測試框架需要使用專門的異常(可能用於攜帶附加資訊),則必須為此異常建立子類,以便與框架“公平地”互動。此屬性的初始值為 AssertionError

longMessage

此類屬性決定當自定義失敗訊息作為 msg 引數傳遞給失敗的 assertXYY 呼叫時會發生什麼。True 是預設值。在這種情況下,自定義訊息將附加到標準失敗訊息的末尾。當設定為 False 時,自定義訊息將替換標準訊息。

在呼叫 assert 方法之前,可以透過將例項屬性 self.longMessage 分配給 TrueFalse 來在各個測試方法中覆蓋類設定。

類設定在每次測試呼叫之前都會重置。

在 3.1 版本加入。

maxDiff

此屬性控制在失敗時報告差異的斷言方法輸出的差異的最大長度。它預設為 80*8 個字元。受此屬性影響的斷言方法是 assertSequenceEqual()(包括所有委託給它的序列比較方法)、assertDictEqual()assertMultiLineEqual()

maxDiff 設定為 None 意味著差異沒有最大長度。

在 3.2 版本加入。

測試框架可以使用以下方法收集測試資訊

countTestCases()

返回此測試物件表示的測試數量。對於 TestCase 例項,這始終是 1

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.Runnerloop_factory。在子類中用 asyncio.EventLoop 覆蓋,以避免使用 asyncio 策略系統。

在 3.13 版本加入。

async asyncSetUp()

準備測試夾具的方法。此方法在 setUp() 之後呼叫。它在呼叫測試方法之前立即呼叫;除了 AssertionErrorSkipTest 之外,此方法引發的任何異常都將被視為錯誤而不是測試失敗。預設實現不執行任何操作。

async asyncTearDown()

在測試方法呼叫並記錄結果後立即呼叫的方法。此方法在 tearDown() 之前呼叫。即使測試方法引發了異常,此方法也會被呼叫,因此子類中的實現可能需要特別小心地檢查內部狀態。除了 AssertionErrorSkipTest 之外,此方法引發的任何異常都將被視為額外的錯誤而不是測試失敗(從而增加報告錯誤的 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.FunctionTestCase(testFunc, setUp=None, tearDown=None, description=None)

此類別實現了 TestCase 介面中允許測試執行器驅動測試的部分,但不提供測試程式碼可以用於檢查和報告錯誤的方法。它用於使用舊版測試程式碼建立測試用例,允許將其整合到基於 unittest 的測試框架中。

分組測試

class unittest.TestSuite(tests=())

此類別表示單個測試用例和測試套件的聚合。該類別提供了測試執行器所需的介面,允許它像任何其他測試用例一樣執行。TestSuite 例項的執行與遍歷套件,單獨執行每個測試相同。

如果提供了 tests,它必須是單個測試用例或其他測試套件的可迭代物件,用於初始構建套件。提供了其他方法來稍後向集合新增測試用例和套件。

TestSuite 物件行為與 TestCase 物件非常相似,只是它們實際上不實現測試。相反,它們用於將測試聚合到應該一起執行的測試組中。一些額外的方法可用於向 TestSuite 例項新增測試

addTest(test)

向套件新增一個 TestCaseTestSuite

addTests(tests)

將來自 TestCaseTestSuite 例項的可迭代物件中的所有測試新增到此測試套件。

這等同於遍歷 tests,併為每個元素呼叫 addTest()

TestSuiteTestCase 共享以下方法

run(result)

執行與此套件關聯的測試,將結果收集到作為 result 傳遞的測試結果物件中。請注意,與 TestCase.run() 不同,TestSuite.run() 要求傳入結果物件。

debug()

執行與此套件關聯的測試而不收集結果。這允許測試引發的異常傳播到呼叫者,並可用於支援在偵錯程式下執行測試。

countTestCases()

返回此測試物件表示的測試數量,包括所有單個測試和子套件。

__iter__()

透過 TestSuite 分組的測試始終透過迭代訪問。子類可以透過覆蓋 __iter__() 來延遲提供測試。請注意,此方法可以在單個套件上多次呼叫(例如在計數測試或比較相等性時),因此在 TestSuite.run() 之前重複迭代返回的測試對於每次呼叫迭代必須相同。在 TestSuite.run() 之後,除非呼叫者使用覆蓋 TestSuite._removeTestAtIndex() 以保留測試引用的子類,否則呼叫者不應依賴此方法返回的測試。

3.2 版中已更改: 在早期版本中,TestSuite 直接訪問測試而不是透過迭代,因此覆蓋 __iter__() 不足以提供測試。

3.4 版中已更改: 在早期版本中,TestSuiteTestSuite.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 例項,或返回 TestCaseTestSuite 例項的可呼叫物件。這些檢查按此處列出的順序應用;也就是說,可能測試用例類中的方法將被識別為“測試用例類中的測試方法”,而不是“可呼叫物件”。

例如,如果您有一個模組 SampleTests 包含一個 TestCase 派生類 SampleTestCase,其中包含三個測試方法(test_one()test_two()test_three()),則指定符 'SampleTests.SampleTestCase' 將導致此方法返回一個執行所有三個測試方法的套件。使用指定符 'SampleTests.SampleTestCase.test_two' 將導致它返回一個僅執行 test_two() 測試方法的測試套件。指定符可以引用尚未匯入的模組和包;它們將作為副作用匯入。

該方法可以選擇性地相對於給定的 module 解析 name

3.5 版中已更改: 如果在遍歷 name 時發生 ImportErrorAttributeError,則將返回一個合成測試,該測試在執行時會引發該錯誤。這些錯誤包含在 self.errors 累積的錯誤中。

loadTestsFromNames(names, module=None)

loadTestsFromName() 類似,但接受一個名稱序列而不是單個名稱。返回值是一個測試套件,支援為每個名稱定義的所有測試。

getTestCaseNames(testCaseClass)

返回在 testCaseClass 中找到的方法名稱的排序序列;這應該是 TestCase 的子類。

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* 方法中對方法名進行排序時使用的比較函式。

suiteClass

一個可呼叫物件,它根據測試列表構建一個測試套件。不需要結果物件上的任何方法。預設值是 TestSuite 類。

這會影響所有 loadTestsFrom* 方法。

testNamePatterns

Unix shell 風格的萬用字元測試名稱模式列表,測試方法必須匹配這些模式才能包含在測試套件中(參見 -k 選項)。

如果此屬性不為 None(預設值),則所有要包含在測試套件中的測試方法都必須與此列表中的某個模式匹配。請注意,匹配總是使用 fnmatch.fnmatchcase() 執行,因此與傳遞給 -k 選項的模式不同,簡單的子字串模式必須使用 * 萬用字元進行轉換。

這會影響所有 loadTestsFrom* 方法。

在 3.7 版本加入。

class unittest.TestResult

這個類用於編譯有關哪些測試成功哪些測試失敗的資訊。

一個 TestResult 物件儲存一組測試的結果。TestCaseTestSuite 類確保結果被正確記錄;測試作者不需要擔心記錄測試結果。

基於 unittest 構建的測試框架可能希望訪問執行一組測試生成的 TestResult 物件以進行報告;TestRunner.run() 方法為此目的返回一個 TestResult 例項。

TestResult 例項具有以下屬性,這些屬性在檢查一組測試執行結果時會很有用

errors

一個包含 TestCase 例項和格式化回溯字串的 2 元組列表。每個元組表示引發意外異常的測試。

failures

一個包含 TestCase 例項和格式化回溯字串的 2 元組列表。每個元組表示使用 斷言方法 顯式發出失敗訊號的測試。

skipped

一個包含 TestCase 例項和跳過測試原因字串的 2 元組列表。

在 3.1 版本加入。

expectedFailures

一個包含 TestCase 例項和格式化回溯字串的 2 元組列表。每個元組表示測試用例的預期失敗或錯誤。

unexpectedSuccesses

一個包含 TestCase 例項的列表,這些例項被標記為預期失敗,但卻成功了。

collectedDurations

一個包含測試用例名稱和浮點數(表示每個已執行測試的耗時)的 2 元組列表。

3.12 新版功能.

shouldStop

當測試執行應該由 stop() 停止時,設定為 True

testsRun

到目前為止執行的測試總數。

buffer

如果設定為 true,sys.stdoutsys.stderr 將在呼叫 startTest()stopTest() 之間進行緩衝。收集到的輸出只有在測試失敗或出錯時才會回顯到真實的 sys.stdoutsys.stderr。任何輸出也會附加到失敗/錯誤訊息中。

在 3.2 版本加入。

failfast

如果設定為 true,stop() 將在第一次失敗或錯誤時被呼叫,從而停止測試執行。

在 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)

當測試用例 testexpectedFailure() 裝飾器標記,但卻成功時呼叫。

預設實現將測試附加到例項的 unexpectedSuccesses 屬性中。

addSubTest(test, subtest, outcome)

當一個子測試完成時呼叫。test 是與測試方法對應的測試用例。subtest 是描述子測試的自定義 TestCase 例項。

如果 outcomeNone,則子測試成功。否則,它會因異常而失敗,其中 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)

一個基本的測試執行器實現,將結果輸出到流。如果 streamNone (預設值),則 sys.stderr 用作輸出流。此類具有一些可配置引數,但本質上非常簡單。執行測試套件的圖形應用程式應提供替代實現。此類實現應接受 **kwargs,因為當向 unittest 新增功能時,構建執行器的介面會發生變化。

預設情況下,此執行器會顯示 DeprecationWarningPendingDeprecationWarningResourceWarningImportWarning,即使它們預設被忽略。此行為可以透過使用 Python 的 -Wd-Wa 選項(參見警告控制)並保留 warningsNone 來覆蓋。

版本 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 的主要公共介面。此方法接受 TestSuiteTestCase 例項。透過呼叫 _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)

failfastcatchbreakbuffer 引數與同名的 命令列選項 具有相同的效果。

warnings 引數指定在執行測試時應使用的警告過濾器。如果未指定,如果 python 傳遞了 -W 選項(請參閱警告控制),則它將保持 None,否則它將設定為 'default'

呼叫 main 會返回一個物件,其 result 屬性包含作為 unittest.TestResult 執行的測試結果。

版本 3.1 中的變化: 添加了 exit 引數。

版本 3.2 中的變化: 添加了 verbosityfailfastcatchbreakbufferwarnings 引數。

版本 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

所有測試執行後,會執行最終的 tearDownClasstearDownModule

請注意,共享夾具與(潛在的)測試並行化等功能配合不佳,它們會破壞測試隔離。應謹慎使用它們。

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()

如果您希望呼叫基類上的 setUpClasstearDownClass,則必須自己向上呼叫它們。TestCase 中的實現為空。

如果在 setUpClass 期間引發異常,則類中的測試將不執行,並且 tearDownClass 將不執行。跳過的類將不執行 setUpClasstearDownClass。如果異常是 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.removeResult(result)

刪除已註冊的結果。一旦結果被刪除,在響應 Control-C 時將不再對該結果物件呼叫 stop()

unittest.removeHandler(function=None)

在不帶引數呼叫時,此函式將刪除 Control-C 處理程式(如果已安裝)。此函式還可以用作測試裝飾器,以在測試執行期間臨時刪除處理程式

@unittest.removeHandler
def test_signal_handling(self):
    ...