文章目錄
- unittest --- Unit testing framework
unittest — Unit testing framework
源碼地址:Lib/unittest/__init__.py
(如果你已經熟悉了測試的基本概念,可以直接跳轉到斷言方法表。)
unittest 單無測試框架是源自於JUnit並且具備其他語言中的主流測試框架的特點。它支持自動化測試、測試中共享設置和關閉代碼、將測試集成到集合中,並將測試從報告框架中獨立出來。
爲了實現這些,unittest通過面向對象的方法來實現許多重要的概念:
- test fixture (測試夾具)
估且翻譯成測試夾具,作用是測試的預設置。一個test fixture代表了一個或多個測試運行時需要的準備工作。例如,它可能包含創建臨時的或代理的數據庫、目錄或啓動一個服務器進程等。 - test case (測試用例)
每個test case都是一個獨立的測試單元。它檢測一個特定的輸入所對應的輸出。unittest提供了一個基類TestCase
,可以使用它來創建test case。 - test suite (測試集合)
test suite是test case、test suite或者兩者都包含的集合體。它的作用是將需要一起執行的測試集成在一起。 - test runner (測試執行器)
test runner是協調執行測試並將輸出提供給用戶的組件。執行器可能是一個圖形交互,一個文本交互或者返回一個特別的值來表明測試的執行結果。
1. Basic example
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'])
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
通過unittest.TestCase
的子類來創建用例。這3個獨立的測試是由三個以test
字母開頭的方法定義的。這種命名方式是爲了告知測試執行器哪些方法代表測試。
這些測試的核心是調用assertEqual()
來檢查是否是預期的結果;調用assertTrue()
或assertFalse()
來判斷狀態;調用assertRaise()
來判定是否指定的異常被拋出。這些方法都使用了assert
(斷言)聲明,這樣測試執行器能統計所有的測試結果,並生成報告。
setup()
和tearDown()
方法可以讓你在測試開始之前和結束之後定義一些要執行的命令。在章節Organizing test code中會詳細介紹他們。
代碼中的最後一部分是一個簡單地運行測試的方法。unittest.main()
爲測試腳本提供了一個命令行的交互方式。當在命令行中運行時,上述腳本的輸入如下所示:
在腳本中傳入參數-v
將使unittest.main()
(將腳本最後一行改爲unittest.main(argv=['Untitled.py', '-v'])
)產生更高級別的反饋,並生成如下輸出:
上述範例展示了unittest功能的最普通的使用,這些已經可以滿足遇到的大多數日常測試需求了。剩下的文檔從原理出發拓展了完整的功能。
2. Command-Line Interface(命令行交互)
unittest模塊能夠直接在命令行中使用來測試模塊、類、甚至是單獨的測試方法。
python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unitest test_module.TestClass.test_method
你可以在一條命令中同時包含任意的模塊名和符合規則的類名與方法名。
測試模塊也可以是指定的文件路徑:
python -m unittest tests/test_something.py
你也可以使用完整的shell文件名來指定要測試的模塊。但是指定的文件必須是可以被作爲模塊導入的。將路徑通過移除.py
的方式轉換成模塊名,並且將路徑用.
隔開。例如將上一條例子改爲python -m unittest tests.test_something
。但如果你的測試文件是不可以作爲模塊導入的,你仍需要以目錄的形式輸入。
你可以通過傳入-v
參數使測試運行時展示更詳細的信息(更高級的反饋):
python -m unittest -v test_module
當執行的命令不含參數時,會開始進行批量測試:
python -m unittest
顯示所有的命令行參數:
python -m unittest -h
1. Command-line options
unittest支持以下這些參數:
-
-b, --buffer
將測試運行過程中的標準輸出與標準錯誤流緩存。測試通過時這些緩存信息會被丟棄。測試失敗或錯誤時,這些信息會被正常打印到失敗信息中。 -
-c, --catch
在測試運行中使用Control-c
,使測試停止在當前測試完成後並生成當前已測試部分的結果。再次Control-c
將會正常拋出KeyboardInterrupt異常。
可以查看Signal Handling中的提供此功能的相關函數。 -
-f, --failfast
測試中遇到第一個錯誤或失敗時就停止。 -
–locals
這些命令行參數也可以用於批量測試中,用來測試整個項目或者一個子集。
3. Test Discovery(批量測試)
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_directroy -p "*_test.py"
python -m unittest discover project_directory "*_test.py"
也可以傳入一個包名來作爲啓動目錄,例如myproject.subpackage.test
。你提供的包名將被導入並且他在文件系統中的位置將會被作爲啓動目錄。
注意:批量測試是通過導入測試文件來加載他們。當批量測試從你指定的啓動目錄中找到了所有的測試文件,它會將路徑轉換成包名進行導入。例如,foo/bar/baz.py
將會被轉化成foo.bar.baz
導入。
如果你有一個已經加載了的全局包,而批量測試這個包在另一個地方的副本,那麼這個導入過程可能會發生在錯誤的地方。如果發生了這種情況,批量測試將會發出警告並退出。
如果你以包名的方式而不是目標路徑的形式提供了啓動目錄,那麼批量測試將會認爲所有位置的包都是你的目標,那麼將不會發出警告。
測試的模塊和包可以通過load_tests protocol來自定義測試加載和發現。
4. Organizing test code(組織測試代碼)
單元測試的基本組成結構是test case(測試用例) — 一個必須創建並檢查其正確性的假設的情況。在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()
方法。
setUp()
與tearDown()
爲測試代碼提供工作環境,被稱爲fixture。
測試用例根據他們各自的測試特點可以進行相應的分組。unittest爲此提供了一個功能:test suite(測試集合),對應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語言編寫的代碼必須要分離開來,那爲什麼不保持一致呢?
- 如果只是測試策略的變更,那沒有必要去修改源碼。
5. Re-using old test code(複用舊的測試代碼)
一些用戶發現他們有一些測試代碼想通過unittest來運行,但裏面的舊測試函數沒有轉換成TestCase
子類方法。
由於這個原因,unittest
提供了一個FunctionTestCase
類。它是TestCase
子類,用來包裝已經存在的測試函數。這裏也是可以提供Set-up和tear-down功能的。
給出如下的測試函數:
def testSomething():
something = makeSomething()
assert something.name is not None
# ...
你可以創建一個等價的測試用例實例,包含了set-up和tesr-down方法的參數,如下:
testcase = unittest.FunctionTestCase(
testSomething,
setUp=makesomethingDB,
tearDown=deleteSomethingDb
)
注意:儘管FunctionTestCase
可以快速地將現有的測試方法轉換成基於unittest的形式,但這種途徑並不推薦。花點時間來創建合適的TestCase
子類將會使將來重構測試時非常地容易。
在有些情況中,現有的測試可能是使用doctest模塊寫的。如果是這樣,doctest提供了一個DocTestSuite
類可以自動根據現有的doctest的測試來建立unittest.TestSuite
實例。
6. Skipping tests and expected failures
uittest支持跳過單獨的甚至是整個類的測試。另外,它可以標記一個測試爲"expected failure",一個測試中斷而且失敗了,但是不會作爲一次失敗計算在TestResult
中。
跳過一個測試是一件簡單的事,通過使用skip()
裝飾器,或它的條件判斷變體之一。
基礎的跳過使用如下:(筆者自已修改了代碼,可以在IDEL中運行)
import unittest
import sys
class MyTestCase(unittest.TestCase):
@unittest.skip("demonstrating skipping")
def test_nothing(self):
self.fail("shouldn't happen")
@unittest.skipIf(int(sys.version[0]) >= 3, "not supported in this library version")
def test_format(self):
# Tests that work for olny 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
下面展示了在verbose模式下的運行輸出:
跳過類的方法與跳過函數是一樣的:
@unittest.skip("showing class skipping")
class MyskippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
TestCase.setUp()
也可以跳過測試。當需要被創建的資源不存在時,這是很有用的。
expected failure使用expectedFailure()
裝飾器。
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
當想要跳過測試時,通過調用skip()
來創建自己的skip裝飾器是很簡單的。下面這個例子中的裝飾器將會跳過測試,除非傳入的obj參數確實包含了重要屬性attr:
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)
無條件地跳過裝飾的測試。最好將爲什麼跳過的理由描述出來。 -
@unittest.skipIf(condition, reason)
跳過裝飾的測試,如果condition的結果是true。 -
@unittest.skipUnless(condition, reason)
跳過裝飾的測試,除非condition的結果是true。 -
@unittest.expectFailure
爲測試標記一個預期失敗。如果測試運行失敗,它將不會作爲失敗爲統計。 -
exception unittest.SkipTest(reason)
拋出此異常用來跳過測試。
通常你可以使用TestCase.skipTest()或者上述跳過裝飾器之一來代替拋出異常的情況。
跳過的測試setUp()
或tearDown()
將不會運行。跳過類setUpClass()
或tearDownClass()
將不會運行。跳過模塊setUpModule()
或tearDownModule()
將不會運行。
7.Distinguishing test iterations using subtests(使用subtests來區分每次迭代)
當你的一些測試之間只有些許細微的差異時,例如一些參數不同,通過在測試主體中使用subTest()
上下文管理函數,unittest可以讓你區分他們。
例如下面這個測試:
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)
生成如下的結果:
如果不使用subtest,程序會在第一個錯誤發生成就終止,而且由於i
的值不會被顯示,也難於診斷出錯誤。
8. Classes and functions(類與方法)
此章節深度描述了unittest的API
1. Test cases
class unittest.TestCase(methodName=‘runTest’)
TestCase
類的實例代表了unittest體系裏的邏輯測試單元。該類通常被作爲基類作用,通過構造子類來實現指定的測試。這個類包含了測試執行器所需要的接口,包括控制測試、測試代碼要去檢查的被測方法和各種各樣的失敗報告。
每個TestCase
實例都會執行一個基本方法:名爲methodName的方法。在使用TestCase
的大多數情況中,你既不會改變methodName也不會重寫默認的runTest()
方法。
python3.2版本中的變動:TestCase
在不提供methodName時成功地實例化。這樣你可以更輕鬆地在交互式解釋器中對TestCase
進行試驗。
TestCase
實例提供了三種類型的方法:第一種用來運行測試,第二種用在測試完成後檢查條件和報告失敗,最後是一些查詢函數可以收集將測試本身的一些信息。
第一類方法(運行測試):
-
setUp()
調用此方法來準備測試fixture。它們在測試方法被調用之前被調用。不同於AssertionError
或者SkipTest
,此方法拋出的任務異常都會被當作錯誤,而不是測試失敗。此方法默認情況下爲pass。 -
tearDown()
當測試方法已經被調用且結果被記錄後,此方法被調用。即使測試方法拋出異常,此方法仍然會被調用,所以在子類中實現時要特別注意檢查它的內部狀態。不同於AssertionError
或SkipTest
,此方法拋出的任意異常將被作爲一個額外的錯誤而不是一次測試失敗(這樣報告的錯誤總數會增加)。此方法只有在setUp()
方法成功運行後被調用,與測試方法的結果無關。此方法默認情況下爲pass。 -
setUpClass()
在一個類中的測試運行之前被調用的類方法。setUpClass
被調用時只有一個參數就是類本身並且必須由classmethod
方法裝飾。@classmethod def setUpClass(cls): ...
在Class and Module Fixtures中查看更多詳情。
-
tearDownClass()
在一個類中的測試運行之後被調用的類方法。tearDownClass
被調用時只有一個參數就是類本身並且必須由classmethod
方法裝飾。@classmethod def tearDownClass(cls): ...
在Class and Module Fixtures中查看更多詳情。
-
run(result=None)
運行測試,收集結果放入作爲結果的TestResult
對象中。如果結果被省略或者是None,一個臨時的結果對象會被創建(通過調用defaultTestResult()
方法)並使用。結果對象返回run()
的調用者。
簡單地調用TestCase
實例可以有同樣的效果。 -
skipTest(reason)
在測試方法或setUp()
方法中調用此方法將跳過當前測試。查看Skipping tests and expected failures 瞭解更多信息。 -
subTest(msg=None,**params)
將一個封閉的代碼塊作爲子測試執行,並返回他的上下文管理器。msg和params是可選的、任意的值,將會在子測試失敗時顯示,可以讓你更清楚的定位異常。
一個測試用例可以包括任意數量的subtest聲明,而且他們可以隨意嵌套。
查看Distinguishing test iterations using subtests來獲取更多信息。 -
debug()
不收集結果的情況下運行測試。它可以將測試拋出的異常傳遞給調用者,並且可用於調試器中運行的測試。
TestCase類提供了多個斷言方法來檢查和報告失敗。下表列出了大多數常用的方法(查看之後的其他表來了解更多assert方法):
Method | Checks that | New in |
---|---|---|
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 |
所有的這些assert方法都可接受一個msg參數,如果被指定,失敗時會作爲錯誤信息(也可以查看longmessage)。注意當assertRaises(),assertRaisesRegex(),assertWarns(),assertWarnsReges()被作爲上下文管理器使用時,msg關鍵字參數會被傳遞給他們。
- assertEqual(first, second, msg=None)
測試first和second是否相等。如果他們不相等,測試將會失敗。
另外,如果first和second是完全相同的類型並且是list、tuple、dict、set、frozenset、str類型或任意由addTypeEqualityFunc()方法記錄了的子類,該方法會被調用來生成更有用的默認錯誤信息(查看list of type-specific methods)。
3.1中更新:增加了自動調用特定類型比較函數。
3.2中更新:assertMultiLineEqual()被作爲默認的類型比較函數用來比較字符串。
- assertNotEqual(first, second, msg=None)
測試first和second是否不相等。如果他們相等,測試失敗。
- assertTrue(expr, msg=None)
assertFalse(expr, msg=NONE)
測試expr是true(或false)。
注意,它是等價於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是否是同一個對象。
- assertIsNone(expr,msg=None)
assertIsNotNone(expr, msg=None)
測試expr是否是None
。
- assertIn(first, second, msg=None)
assertNotIn(first, second, msg=None)
測試first是否在second中。
- assertIsInstance(obj, cls, msg=None)
assertNotIsInstance(obj, cls, msg=None)
測試obj是否是cls(此參數可以一個類或者幾個類的元組,由isinstance()
支持)的一個實例。使用assertIs(type(obj), cls)
來查看準確的類型。
使用下列方法甚至可以檢查生成的異常、警告和日誌信息:
Method | Checks that | New in |
---|---|---|
assertRaises(exc, fun, *args, **kwds) | fun(*args,**kwds) raises exc | |
assertRaisesRegex(exc, r, fun, *args, **kwds) | fun(*args, **kwds) raises exc and the message matches regex r | 3.1 |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds) raises warn | 3.2 |
assertWarnsRegex(warn, r, fun, *args, **kwds) | fun(*args, **kwds) raises warn and the message matches regx r | 3.2 |
assertLogs(logger, lever) | The with block logs on logger with minimum level | 3.4 |
- assertRaises(exception, callable, *args, **kwds)
- assertRaises(exception,*, msg=None)
測試當callable被調用時所拋出的異常,callable函數的參數是傳入assertRaises()中的*args與**kwds。如果拋出的異常是assertRaises()中傳入的exception則測試通過,如果拋出了其他異常則判定爲一個error,如果沒有異常拋出則測試失敗。exception可以是多個異常的集合體或是包含了多個exception類的元組。
如果只給定了必要參數exception和可選參數msg,那函數可以作爲一個上下文管理器使用,如下:
當assertRaises()函數作爲上下文管理器使用時,可以接受關鍵字參數msgwith self.assertRaises(SomeException): do_something()
上下文管理器會將蒱獲的異常對象存放在它的exception
屬性中。如果想對異常進行一些額外的檢查,這樣做是很有用的:with self.assertRaises(SomeException) as cm: do_something() the_exception = cm.exception self.assertEqual(the_exception.error_code, 3)
-
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')
or
with assertRaisesRegex(ValveError, "invalid literal": int('XYZ')
-
assertWarns(*warning, callable, *args, *kwds)
-
assertWarns(warning, *, mag=None)
測試調用callable時觸發了的警告,callable的參數是傳入assertWarns()函數中的*args與*kwds。當指定的warning被觸發時,測試通過。所有的異常都判定爲error。warning可以是多個warning的集合體或是包含了多個warning類的元組。
如果只給定了warning參數或者可選參數mag*,返回一個上下文管理器:with self.assertWarns(SomeWarning): do_something()
上下文管理器會將捕獲的warning對象存放在它的
warning
屬性中,並且觸發警告的代碼行的信息將會存放在它的filename
和lineno
屬性中。這樣便於對捕獲的warning進行一些額外的檢查。with self.assertWarns(SomeWarning) as cm: do_something() self.assertIn('myfile.py', cm.filename) self.assertEqual(320, cm.lineno)
該方法被調用時,忽略warning過濾器。
-
assertWarnsRegex(exception, regex, callable, *args, **kwds)
-
assertWarnsRegex(exception, regex, *, msg=None)
與assertRaisesRegex功能一樣,不過針對對象是warning -
assertLogs(logger=None, level=None)
這是一個上下文管理器,用來測試至少在level這個級別有至少有1條信息記錄在logger或者他的子類。
如果給定了logger,它必須是一個logging.Logger對象或者是該對像的名稱。默認的爲系統級的logger,它會抓取所有的信息。
如果給定了level,它必須是一個表示logging級別的數值或等價的字符串名稱(例如"ERROR"或logging.ERROR)。默認的是logging.INFO。
如果至少有一條符合logger和level的信息發送到了with
塊中,測試通過。
上下文管理器返回對象是一個記錄器,可以保存匹配的日誌信息。它有兩個屬性:- records
匹配的日誌信息的logging.LogRecord對象的列表。 - output
匹配的日誌信息的格式化的字符串輸出組成的列表。
例子: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'])
- records
以下是一些其他的斷言函數:
Methon | Checks than | New in |
---|---|---|
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) | 3.2 |
-
assertAlmostEqual(first, second, places=7, msg=None, delta=None)
-
assertNotAlmostEqual(first, second, places=7, msg=None, delta=None)
測試first和second是否是近似相等的。判定方法是計算first和second的差值,並依據給定的place參數的值(默認爲7)進行四捨五入,然後將這個結果與0進行比較。注意這裏是四捨五入而不是取多少位有效數字。
如果給定了delta參數,將忽略默認的places參數,測試first與second之前的差的絕對值是否小於(或者大於等於)delta的值。
如果強行同時給定places與delta參數,將會拋出TypeError錯誤。 -
assertGreater(first, second, msg=None)
-
assertGreaterEqual(first, second, msg=None)
-
assertLess(first, second, msg=None)
-
assertLessEqual(first, second, msg=None)
測試first是否 >, >=, <, <= second。 -
assertRegex(text, regex, msg=None)
-
assertNotRegex(text, regex, msg=None)
測試一個regex是否匹配text。萬一測試未通過,表達式和text都會包含在錯誤信息中。regex可以是一個與此同時表達式對象或是一個適用於re.search()
函數的包含正則表達式的字符串。 -
assertCountEqual(first, second, msg=None)
測試序列first和second在不考慮順序的情況下擁有相同的元素。如果不相同,將會生成一個列出了兩個序列不同項的錯誤信息。
對兩個序列進行比較時不會忽略重複的無素。它會檢驗兩個序列的元素個數是否相等。
assertEqual()
函數可以檢查兩個相同類型的對象是否相等,由它派生出了一系列只查檢指定類型的函數。這些函數涵蓋了大多數的內置類型,另外我們也可以通過addTypeEqualityFunc()
函數來創建這類函數。
- addTypeEqualityFunc(typeobj, function)
註冊一個將會被assertEqual()調用的判定指定類型的方法來檢查兩個typeobj類型(非子類)的對象是相等的。function必須傳入兩個位置參數和一個msg=None
的關鍵字參數,就是assertEqual()
函數一樣。它必須在兩個參數不相等時拋出self.failureException(msg)
異常,用來說明詳細信息。
下列表中是被assertEqual()函數自行調用的指定類型方法。注意,沒必須去專門調用這些方法。
Method | Used to compare | New in |
---|---|---|
assertMultiLineEqual(a, b) | strings | 3.1 |
assertSequenceEqual(a, b) | sequences | 3.1 |
assertListEqual(a, b) | lists | 3.1 |
assertTupleEqual(a, b) | tuples | 3.1 |
assertSetEqual(a, b) | sets or frozensets | 3.1 |
assertDictEqual(a, b) | dicts | 3.1 |
-
assertMultiLineEqual(first, second, msg=None)
測試多行字符串first和second是相等的。不相等時,它們之間的差異部分會被高亮顯示在錯誤信息中。使用assertEqual()
函數比較string時默認調用該方法。 -
assertSequenceEqual(first, second, msg=None, seq_type=None)
測試兩個序列是相等的。如果指定了seq_type,first和second必須是seq_type所指定的類型,否則將會失敗。如果兩個序列不相等,不相等的部分會在錯誤信息中顯示。
該方法並不直接被assertEqual()
方法引用,而是封裝在assertListEqual()
與assertTupleEqual()
方法中 -
assertListEqual(first, second, msg=None)
-
assertTupleEqual(first, second, msg=None)
測試兩個list或tuple是否相等。如果不相等,不相等的部分會在錯誤信息中顯示。如果它們的類型不是列表或元組也是拋出錯誤。這兩個方法在assertEqual()
函數比較list或tuple時被調用。 -
assertSetEqual(first, second, msg=None)
測試兩個set是相等的。如果不相等,差異的部分會顯示在錯誤信息中。該方法在assertEqual()
函數比較set時被調用。 -
assertDictEqual(first, second, msg=None)
測試兩個dict是相等的。如果不相等,差異的部分會顯示在錯誤信息中。該方法在assertEqual()
函數比較dict時被調用。
最後,TestCase還提供了以下方法及屬性:
-
fail(msg=None)
無條件地將一個測試標註爲失敗的,msg 爲錯誤信息。 -
failureException
該屬性提供了測試方法拋出的異常。如果測試框架需要全用一個特殊的exception,可能是用來攜帶一些額外信息,那麼必須去構那建該屬性的子類。該屬性的初始值是AssertionError
。 -
longMessage
該屬性決定了當一個帶有msg參數的assert方法失敗時程序的反應。默認值是True
,此時msg的信息將會添加到標準失敗信息的末尾。當將它設置爲False
時,msg的信息會替換標準失敗信息。
該屬性可以單獨的測試方法中重新設置,self.longMessage
,在使用assert方法之前設置。每個測試方法執行前會重置該屬性。
如果直接在類下設置longMessage=False
,則會對所有方法一直有效。 -
*maxDiff
該屬性決定了assert方法在失敗信息中反饋差異時的最大長度。默認情況是80*8個字符。受該屬性影響的方法有assertSequenceEqual()
(包括所有調用該方法的方法),assertDictEqual()
和assertMultiLineEqual()
。
將該屬性設置爲None意味着沒有最大長度限制。
測試框架可以使用下列方法來收集測試信息:
-
countTestCases()
返回測試對象所測試的數量。對TestCase
來說,它始終是1。 -
defaultTestResult()
返回一個當前測試測試用例類的測試result類的實例(如果沒有其他的result類提供結run()
方法)。
對TestCase
實例來說,它始終是TestResult
類的實例;TestCase
的子類在必要時應該重寫該方法。 -
id()
返回一個可以標記指定測試用例的字符串。它通常是包含了模塊名及類名的測試方法的全稱。 -
shortDescription()
返回測試的描述,如果沒有提供描述則返回None
。默認返回測試方法下的文件字符串的第一行,如果沒有返回None
。 -
addCleanup(function, *args, **kwargs)
添加一個函數將會在tearDown()
執行後被調用,參數爲addCleanup
函數中的位置參數與關鍵字參數,用來清除測試過程中使用的資函數將會按照與它們添加進去的順序相反順序執行(LIFO)。
如果setUp()
執行失敗,意味着tearDown()
將不會執行,但所有添加的函數仍然會執行。 -
doCleanups()
在tearDown()
執行後或setUp()
拋出異常後,無條件地執行該方法。
該方法是用來調用被addCleanup()
函數添加了的清除函數。如果你想在tearDown()
之前調用該方法,你可以自己主動調用doCleanups()
。
每次調用doCleanups()
會消除cleanup函數棧中的一個函數,因此可以在何何時候調用它。 -
classmethod addClassCleanup(function, /, *args, **kwargs)
-
classmethod doClassCleanups()
-
class unittest.IsolatedAsyncioTestCase(methodName=‘runTest’)
以上三個項爲3.8版本新增,文檔中有不一致處,本人未安裝3.8版本,未進行試驗,在此不翻譯討論。
class unittest.FunctionTestCase(testFunc, setUp=None, tearDown=None, description=None)
該類封裝了TestCase
的部分接口,以允許測試執行器來執行測試,但並沒有提供相關查檢和反饋錯誤的方法。它是用以前的測試代碼來創建測試用例,使它能適用於當前的uniitest測試框架。
廢除的別名
因爲一些歷史原因,一些TestCase
方法用一個或多個如今已經廢棄的別名。下表列出了它們的當前名字和曾用名:
Method Name | Deprecated alias | Deprecated alias |
---|---|---|
assertEqual() | failUnlessEqual | assertEquals |
assertNotEqual() | failIfEqual | assertNotEquals |
assertTrue() | failUnless | assert_ |
assertFalse() | failIf | |
assertRaises() | failUnlessRaises | |
assertAlmostEqual() | failUnlessAlmostEqual | assertAlmostEquals |
assertNotAlmostEqual() | failIfAlmostEqual | assertNotAlmostEquals |
assertRegex() | assertRegexpMatches | |
assertNotRegex() | assertNotRegexpMatches | |
assertRaisesRegex() | assertRaisesRegexp |
2.Grouping tests
class unittest.TestSuite(tests=())
該類代表一個測試用例和測試套件的集合。該類提供了測試執行器所需要的接口,使它能夠像測試用例一樣執行。運行TestSuit
實例就像對套件進行迭代,然後運行每個單獨的測試。
如果提供了tests,它必須是由一些單獨的測試用例和測試套件組成的可迭代對象,可以用來構建測試套件。還有一些額外的方法可以用來添加用例及套件。
TestSuite
對象與TestCase
對象非常相似,除了它們並沒有實現測試。相反,它們只是將需要一起執行的測試集合在一個組中。一些額外的方法可以向TestSuite
實例中添加測試。
- addTest(test)
向測試套件中添加一個TestCase
或TestSuite
- addTests(tests)
向測試套件中添加一個由TestCase
和TestSuite
實例所組成的可迭代對象中的所有測試。
它等同於調用addTest()
方法對tests進行迭代。
TestSuite
與TestCase
共享哪下方法:
-
run(result)
執行套件中的測試,收集測試結果傳遞至測試結果對象result中。注意不同於TestCase.run()
,TestSuite.run()
需要測試結果對象作爲參數。 -
debug()
執行套件中的測試,但不收集結果。它可以使用拋出的異常直接反饋給調用者,並且可以在debug栻上執行測試。 -
countTestCases()
返回當前測試對象中所包含的測試數量,包括單獨的測試用例及子套件。 -
__iter__()
由TestSuite
所集成的測試是可迭代的。子類可以重寫該方法來提供懶惰的測試。注意訪方法在一個套件中可能被調用多次(例如計算測試數量和進行相等比較時),因此在執行TestSuite.run()
之前,每次迭代必須是相同的。在TestSuite.run()
之後,調用者不能信賴該方法的返回值,除非子類中重寫了TestSuite._removeTestAtIndex()
來保持測試的引用。
在一個TestSuite
對象的典型使用中,run()
方法是被TestRunner
引用的而不是終端用戶的組件。
3. Loading and running tests
class unittest.TestLoader
TestLoader
類是用來從類和模塊中創建測試套件的。通常情況下沒必要創建該類的實例;unittest模塊提供了一個可用的unittest.defaultTestLoader
。然而需要定製專用配置時需要使用到子類或實例。
TestLoader
對象用下列屬性:
- errors
加載測試時的非致命錯誤的列表 。加載器在任何時候都不會重置它。致命錯誤會被相關方法作爲異常拋給用戶。
TestLoader
對象有以下方法:
-
loadTestsFromTestCase(testCaseClass)
返回一個由TestCase
派生出來的testCaseClass所包含的所有測試用例所組成的套件。
每一個方法所創建的測試用例由getTestCaseNames()
全名。默認情況下這些方法都是以字符串test開頭(由該類下的testMethodPrefix屬性約定)。如果getTestCaseNames()
沒有返回任何方法,但是testCaseClass已經實現了*runTest()*方法,那麼將爲該方法創建一個單獨的測試用例。 -
loadTestsFromModule(module, pattern=None)
返回給定模塊中的所有測試用例所組成的套件。該方法查找module中所有由TestCase
派生出的類,併爲類中定義的每一個測試方法創建一個測試用命。注意:module可以是包也可以是.py文件,但必須是已使用import導入或使用__import__內置方法所生成的對象注意:雖然通過對
TestCase
類進行層次性派生可以方便地共享fixtures和helper函數,但是在不打算實例化的基類中定義測試方法是不合適的。然而該情況又適用於當子類中需要定義不同的fixtures時。如果模塊中提供了
load_tests
函數,那麼將會使用該函數來加載測試。它可以使模塊能自定義測試加載的方式。[點擊這裏](#load_tests protocol)是加載的規則。partern參數作爲load_tests
方法的第三個參數傳入。 -
loadTestsFromName(name, module=None)
根據給定的name返回包含所有測試用例的suite對象。
指定的name必須是一個點號類名稱即可以是一個模塊,一個測試用例類,測試用例類中的一個測試方法,一個TestSuite
實例,或一個返回TestCase
或TestSuite
實例的可調用對象。按上述列出的順序來分析它是哪種情況,如果它是一個測試用例類中的方法,則不會將它判定爲一個可調用對象。
例如,如果有一個SampleTests
模塊,裏面有一個TestCase派生類SampleTestCase
,這個類又有三個測試方法(test_one(),test_two(),test_thress()),那麼指定name爲SampleTests.SampleTestCase
將會返回包含這三個測試方法的suite對象。而指定SampleTests,SampleTestCase.test_two
將會返回一個只包含這一個方法的suite對象。這個規則適對沒有導入的模塊和包都適用;它們將會被順便導入。
該方法也可以將name按給定的module進行分解。
-
loadTestsFromNames(names, module=None)
與loadTestsFromName()
一樣,除了names是一個序列而不是一個單獨的名稱。返回值是一個包含了names所有指定的對象中所包含的測試。 -
getTestCaseNames(testCaseClass)
返回testCaseClass中所有測試方法的名稱的序列。參數必須是TestCase
類。 -
discover(start_dir, pattern=‘test*.py’, top_level_dir=None)
從指定的start_dir目錄開始遞歸查找所有的測試模塊,返回一個包含它們的TestSuite
對象。只有符合pattern規則的文件纔會被加載。(這裏使用shell的匹配規則)只有可被import的模塊才能被加載。所有的測試模塊必須都可以從項目的頂層目錄所導入。如果啓動目錄不是頂層目錄,那麼頂層目錄必須單獨給出。
如果一個模塊導入失敗,例如發生了語法錯誤,那麼它將被記錄爲一次單獨的錯誤,但批量檢索仍然繼續。如果導入失敗是因爲
SkipTest
,則將會記錄爲一次跳過。如果一個檢索到一個包(目錄下含有__init__.py文件),將會在該文件下查找
load_tests
函數。如果存在,則會調用package.load_tests(loader, tests, pattern)。測試的批量檢索會確保一個包只檢索一次,儘管load_test
函數中又一次調用了loader.discover
。如果包中存在
load_tests
那麼批量檢索將不會再在包中遞歸查找,而是由load_tests
函數來加載包中的測試。pattern參數有意沒有存儲爲loader的屬性,因此包可以繼續自行批量檢索。top_level_dir被存儲爲loader屬性,因此load_tests不用將它傳遞給loader.discover()。
start_dir可以是一個點號形式的模塊名稱,也可是一個路徑。
下列TestLoader
的屬性可以配置子類或實例。
-
testMethodPrefix
測試方法的名稱前綴,符合該規則的方法將被判定爲測試方法。默認爲'test'
。
它作用於getTestCaseNames()
以及所有的loadTestsFrom*()
方法。 -
sortTestMethodsUsing
在getTestCaseNames()
方法以及所有的loadTestsFrom*()
方法中進行名稱排序時使用。 -
suiteClass
用來構建測試方法集的可調用對象。不需要對結果使用任何方法。默認值是TestSuite
類。
影響所有的loadTestsFrom*()
方法。 -
testNamePatterns
3.7中新增
class unittest.TestResult
該類用來編寫反應測試成功及失敗的信息。
一個TestResult
對象存放一個測試集合的所有結果。TestCase
和TestSuite
確保結果被準確地記錄;測試的編寫者不用擔心測試結果的記錄問題。
基於unittest的測試框架希望TestResult
對象可能希望訪問運行測試集合後存儲報告的TestResult
對象;由TestRunner.run()
方法返回的TestResult
實例可以實現這個目的。
TestResult
實例的以下這些屬性對於查看測試結果是很有用的:
-
errors
一個以二元元組爲元素所組成的列表,元組內的每一個元素爲TestCase
實例,第二個元素爲已轉換爲字string格式的該實例執行時的回溯信息。每個元組代表一個發生了非預期異常的測試。 -
failures
一個以二元元組爲元素所組成的列表,元組內的每一個元素爲TestCase
實例,第二個元素爲已轉換爲字string格式的該實例執行時的回溯信息。每個元組代表一個發生由TestCase.assert*()
方法所導致的失敗。 -
skipped
一個以二元元組爲元素所組成的列表,元組內的每一個元素爲TestCase
實例,第二個元素爲該測試跳過的原因。 -
expectedFailures
一個以二元元組爲元素所組成的列表,元組內的每一個元素爲TestCase
實例,第二個元素爲已轉換爲字string格式的該實例執行時的回溯信息。每個元組代表一個發生了預期失敗的用例。 -
unexpectedSuccess
一個列表,元素爲一個標記爲預期失敗但執行成功了的用例。 -
shouldStop
當測試需要執行stop()
方法來終止時,將該屬性設置爲True
。 -
testsRun
目前爲止已運行的測試用例的總合。 -
buffer
如果設置爲True
,sys.stdout
和sys.stderr
將會在startTest()
執行後,stopTest()
執行前緩存。如果測試失敗或錯誤,收集的輸出會由實際sys.stdout
和sys.stderr
打印出來。(代碼中此時是由_original_std*屬性的write方法實現)。所有的輸出也會符加上失敗或錯誤信息。 -
failfast
如果設置爲True
,當發生第一個錯誤或失敗時會調用stop()
方法,停止測試。 -
tb_locals
如果設置爲True
,局部變量將會在回溯中顯示。 -
wasSuccessful()
如果當前所有的測試都通過了,返回True
,否則返回False
。
3.4版本中變更:如果標記了expectedFailure
的方法發生了unexpectedSuccess
,也返回False
。 -
stop()
調用該方法來設置shouldStop
屬性爲True
來表明測試應該中止。TestRunner
對象應該檢查該標記,並不再執行任何其他測試。例如,當用戶使用鍵盤來給出一箇中斷信號時,
TextTestRunner
類使用該特性來停止測試框架。提供了TextRunner
組件的交互工具可以使用一個類似的方法來實現這個功能。
下列TestResult
類中的方法用來保證內部的數據結構完善性,可以在子類中擴展來支持額外的測試報告需求。在創建在測試執行中支持交互性反饋的工具時是特別有用的。
-
startTest(test)
當一個測試用例準備執行時被調用。 -
stopTest(test)
不考慮測試結果,在一個測試用例執行完畢後調用。 -
startTestRun()
在整個測試開始之前,調用一次。 -
stopTestRun()
在整個測試結束後,調用一次。 -
addError(test, err)
當測試用例拋出一個非預期的異常時調用。err是由sys.exc_info()
返回的元組:(type, value, traceback)
。默認的功能是向實例的
errors
屬性中添加一個元組(test, formatted_err)
,formatted_err是由err而來的格式化的回溯信息。 -
addFailure(test, err)
當測試用例發生失敗時調用。err是由sys.exc_info()
返回的元組:(type, value, traceback)
。默認的功能是向實例的
failures
屬性中添加一個元組(test, formatted_err)
,formatted_err是由err而來的格式化的回溯信息。 -
addSuccess(test)
測試用例執行成功時調用。
默認爲pass。 -
addSkip(test, reason)
當測試用例被跳過時調用。reason作爲調過的原因說明。默認功能是向實例的
skipped
屬性中添加一個元組(test, reason)
。 -
addExpectedFailure(test, err)
當一個測試用例執行失敗,但是被裝飾了expectedFailure()
時調用。默認的功能是向實例的
expectedFailures
屬性中添加一個元組(test, formatted_err)
,formatted_err是由err而來的格式化的回溯信息。 -
addUnexpectedSuccess(test)
當一個測試用例被裝飾了expectedFailure()
但卻執行成功時調用。默認功能是向實例的
unexpectedSuccesses
屬性中添加該測試用例對象。 -
addSubTest(test, subtest, err)
當一個子測試完成時調用。test是當前由測試方法所構建的用例。subtest是一個描述子測試的TestCase
實例。(它是_SubTest
類的實例,該類是TestCase
的子類)。如果err是
None
,表示子測試執行通過。否則,子測試失敗,err則是執行失敗後收sys.exc_info()返回的元組:(type, value, traceback)
該方法默認在子測試通過時不做任何時,當子測試失敗時將它記錄爲一個標準的失敗。
class unittest.TextTestResult(stream, descriptions, verbosity)
被TestTestRunner
類所引用的改裝後的TestResult
子類。
unittest.defaultTestLoader
被共享的TestLoader
類的實例。如果不需要自定義TestLoader
,可以使用該實例來避免反覆創建新的實例。
class unittest.TextTestRunner(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False)
一個基本的測試執行器工具,用來將結果輸出到數據流。如果steam是None
,默認情況下sys.stderr
將會作爲輸出流。該類有很多配置參數,但其實都很簡單。執行測試套件的圖形界面應用應該提供靈活的工具。當需要向unittest框架中添加新特性時,這個工具應該接受**kwargs
參數來用於修建執行器。
執行器默認會顯示DeprecationWarning
,PendingDeprecationWarning
,ResourceWarning
和ImportWarning
,儘管在默認情況下這些警告會被忽略。DeprecationWarning
是由unittest的已廢棄的方法所引起的,當warnings是default
或always
時,它們在每個模塊執行時只顯示一次,這是爲了避免出現太多的警告信息。通過在使用時使用Python的-Wd
或-Wa
參數和設置warning爲None
,這個功能可以被覆蓋_。
-
_makeResult()
該方法是在TextTestRunner
類的run()
方法用調用,用來創建一個TestResult
實例。它不可以被直接調用,但是可以重寫該方法用來提供自定義的TestResult
類。_makeResult()
實例化了TextTestRunner
類的resultclass
參數。默認情況下,如果沒有提供resultclass
參數,result類由以下參數來實例化
stream, descriptions, verbosity
-
run(test)
該方法是TextTestRunner
核心公共接口。該方法需要一個TestSuite
或TestCase
實例作爲參數。通過調用_makeResult()
方法來創建TestResut
實例,然後測試執行並將結果打印到標準輸出中(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找到的所有測試。如果值爲None
或不指定,那它的值是sys.argv
。
argv參數是傳遞給程序的參數列表,它的第一個元素應該是項目名。如果值爲None
或不指定,那它的值是sys.argv
。
testRunner參數可是一個測試執行器的類,也可以是它的實例。默認情況下,main
會調用sys.exit()
,並傳入狀態碼來判定測試執行成功還是失敗。
testloader參數必須是一個TestLoader
實例,並且默認爲defaultTestLoader
main
支持在交互式編譯器使用它時傳入exit=False
參數。它可以在顯示結果時不調用sys.exit()
:
from unittest import main
main(module='test_module', exit=False
failfast, * catchbreak* 和buffer參數與命令行參數-f,-c,-b是同樣的作用。
warning參數指定了測試運行時的警告過濾器。如果沒有指定則爲None
,如果在python命令行中給定了參數-w則將被設置爲default
。
調用main
實際上是返回一個TestProgam
類的實例。它將測試執行的結果存儲在`result·屬性。
load_tests protocol
通過實現一個名爲load_tests
的函數,模塊或包可以在測試運行時自定義加載測試的方式。
如果一個模塊中定義了load_tests
函數,那麼那他將被TestLoader.loadTestFromModule()
按以下格式調用:
load_tests(loader, standard_tests, pattern)
pattern是由loadTestsFromModule
中傳入。默認值爲None。
load_tests
必須返回一個TestSuite
實例。
loader是TestLoader
的實例對象。standard_tests
是在默認情況下將會從模塊中加載的對象。通常情況下,只會從模塊的標準測試對象集中添加或刪除測試。第三個用來批量檢索時的篩選。
一個典型的load_tests
函數,用來從一些指定的TestCase
類中來加載測試可能是像下面這樣:
test_case = (TestCase1, TestCase2, TestCase3)
def load_tests(loader, tests, pattern):
suite = TestSuiite()
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
文件中的測試)
由於pattern傳入了load_tests
函數中,因此包可以持續地檢索測試。一個不做任何事的load_tests
函數像下面這樣:
def load_tests(loader, standard_tests, pattern):
this_dir = os.path.dirname(__file__)
package_tests = loader.discover(start_dir=this_dir, pattern=pattern)
standard_tests.addTests(package_tests)
return standard_tests
個人小結:
- load_tests函數可以讓我們自定義加載測試的方式, load_tests必須返回一個TestSuite實例
- 針對模塊的load_tests定義在單個模塊文件中,
standard_tests
參數默認爲模塊中檢索所有測試對像的集合,其結構爲:suite=[suite1,suite2…],suite都是TestSuite實例,內層的suite1等對象是由loader.loadFromTestCase(TestCase)返回的實例。 - 針對包的load_tests定義在包的__init__.py文件中,
standard_tests
默認爲檢索__init__.py模塊所返回的suite。當前層次的load_tests函數將作用於下層所有包中。當前工作目錄下的__init__.py中的load_tests並不會執行。
4. 類和模塊的fixtures
類和模塊級別的fixtures封裝在TestSuite
中。當測試套件遇到一個從新的類中解析出來的測試時,那麼上一個類的tearDownClass()
將會被調用,接下來調用新的類的setUpClass()
。
相同的,如果一個測試來自一個新的模塊,那麼將運行上一個模塊的tearDownModule
,然後運行新模塊的setUpModule
。
在所有的測試運行完畢後,將會運行tearDownClass
和tearDownModule
。
注意共享的fixtures並不適合一些潛在的特性,像並行測試和打斷測試,它們應該被小心地使用。
默認的測試順序是由unittest測試加載器創建的測試按照相同的模塊和類分組。這可以使setUpClass
/setUpModule
在每個類和模塊中只調用一次。如果將順序打亂,那麼相鄰的測試可能來自不同的模塊和類,那麼在一個單獨的測試用例中它們的共享fixture函數可能被調用多次。
共享fixture並不適用於非標準順序的套件。如果不想使用共享fixture,框架中仍有BaseTestSuite
可以使用。
如果共享fixture運行時拋出異常,則測試結果將被作爲error
。因爲沒有相應的測試實例化一個_ErrorHolder
(和TestCase
有相同的接口)對象來代表這個錯誤。如果你只是使用標準的unittest測試執行器,那麼這些都無關緊要,但如果是你一個框架的作者,這可能是相關的。
1. setUpClass和tearDownClass
它們必須封裝成類方法的形式:
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._connection = createExpensiveConnectionObject()
@classmethod
def tearDown(cls):
cls._connection.destroy()
如果希望setUpClass
和tearDownClass
在基類中被調用,那麼你必須自己構建它們。在TestCase
中它們是沒有內容的。
如果setUpClass
運行期間拋出了一個異常,那麼該類中的所有測試將不再運行,tearDownClass
也不會運行。跳過的類不會運行setUpClass
和tearDownClass
。如果該異常是SkipTest
類型,那麼測試類將會反饋一個skipped而不是一個error。
2. setUpModule 和 tearDownModule
它們應該作爲封閉爲函數:
def setUpModule():
createConnection()
def tearDownModule():
createConnection()
如果setUpModule
運行期間拋出了一個異常,那麼該模塊中的所有測試將不再運行,tearDownModule
也不會運行。如果異常是SkipTest
類型,那麼測試類將會反饋一個skipped而不是一個error。
使用addModuleCleanup
來添加清理的代碼,即使用例拋出的異常,它也一定會運行。
unittest.addModuleCleanup(function,/, *argv, **kwargs)
添加一個在tearDownModule
運行之後被調用的函數來清理測試期間使用的資源。函數的調用順序與它們添加的順序相反(LIFO)。它們被調用時所使用的參數由addModuleCleanup()
在添加它們時傳入。
如果 setUpMoudle()
失敗,意味着不會調用tearDownModule()
,但是清理函數仍然會調用。
python3.8版本新增
unittest.doModuleCleanups()
該方法在tearDownModule()
之後被無條件地調用,即使setUpModule
拋出一個異常。
它是用來調用所有被addCleanupModule()
所添加的函數。如果希望在tearDownModule()
之前調用它,你需要自己來調用。
doModuleCleanups()
每調用一次就會從棧中推出一個清理函數並執行它,所以可以在任何時候調用它。
python3.8版本新增
9. 信號控制
unittest的命令行參數-c/–catch,和unittest.main()的catchbreak
參數,爲測試的運行提供了一個更友好控制control-C方式。擁有catchbreak特性的control-C允許正在運行的測試用例運行完畢後再停止測試,然後報告目前爲止的所有測試結果。如果再次使用control-c將會拋出一個普通的KeyboardInterrupt
異常。
control-c信號控制器希望仍然兼容那些安裝了自己的signal.SIGINT控制器的代碼和測試。如果調用unittest的控制器而不是已配置的signal.SIGINT控制器,即被測試系統替換並授權,然後調用其默認的控制器。替換一個已安裝的控制器並授權通常只是代碼的一個額外動作。對單個需要無效化control-c控制的可以使用removeHandler()
裝飾器。
有一些對框架作者來說有用的函數,可以讓測試框架有效地進行control-c控制。
unittest.installHandler()
安裝control-c控制器。當收到一個signal.SIGINT(通常由用戶使用control-c時產生)。所有標記的result對象都將調用stop()
方法。
unittest.registerResult(result)
爲control-c控制標記一個TestResult
對象。當標記一個result時,會儲存它的一個弱引用,因此它並不能防止result收集無用的數據。
當control-c控制未啓用時,標記一個TestResult
對象是無副作用的,因此測試框架可以無條件地標記所有各自獨立的result對象,不管它們是否啓用了控制器。
unittest.removeResult(result)
移除一個result對象的標記。當result對象的標記被移除時,control-c將不會再調用該result對象的stop()
方法。
unittest.removeHandler(function=None)
當沒有參數傳入時,該方法會移除已安裝的control-c控制器。該函數也可以作爲裝飾器,在測試執行時臨時地移除控制器:
@unittest.removeHandler
def test_signal_handling(self):
...