單元測試框架
1 unittest框架
unittest的設計靈感最初來源於Junit以及其他語言中具有共同特徵的單元框架。它支持自動化測試,在測試中使用setup(初始化)和shutdown(關閉銷燬)操作,組織測試用例爲套件(批量運行),以及把測試和報告獨立開來。
爲了實現這些,unittest以一種面向對象的方式產生了一些很重要的概念:
- test fixture
fixture表示tests運行前需要做的準備工作以及結束後的清理工作。比如,創建臨時/代理數據庫、目錄或啓動一個服務器進程。
- test case
test case是單元測試中的最小個體。它檢查特定輸入的響應信息。unittest提供了一個基礎類:TestCase,用來創建test case。
- test suite
test suite是test case的合集,通常用test suite將test case彙總然後一起執行。
- test runner
test runner是一個執行器,它可以執行case並提供結果給用戶。它可以提供圖形界面、文本界面或者返回一個值表示測試結果。
1.1 示例
unittest
模塊提供了豐富的工具,用於組織和運行tests。本節演示了其中一小部分功能,但足以滿足大多數用戶的需要。
import unittest
class TestStringMethods(unittest.TestCase):
def setUp(self):
"""
執行每一個測試方法前都會執行
:return:
"""
pass
@classmethod
def setUpClass(cls) -> None:
"""
只運行一次
該方法會首先執行, 爲測試方法做準備工作
:return:
"""
# 文件保存路徑
cls.save_path = pathlib.Path('./results/')
cls.save_path.mkdir(exist_ok=True)
def tearDown(self):
"""
該方法會在每個測試方法執行完畢後執行, 爲測試後做收尾工作
:return:
"""
print('tearDown')
@classmethod
def tearDownClass(cls) -> None:
"""
只執行一次
該方法會在所有測試方法執行完畢後執行, 爲測試後做收尾工作
:return:
"""
pass
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的子類創建了一個case。其中有三個test開頭的測試方法,這種命名方式可以告訴執行器(test runner)哪些方法可以被執行。也就是說test開頭的測試方法纔會被執行。
這些測試方法中都使用了assertEqual()來判斷預期值,使用assertTrue()和assertFalse()來驗證條件,或使用assertRaises()來驗證某個具體的被拋出的異常。這些斷言方法經常被用來替代assert,這樣執行完成後,所有的測試結果都會被記錄並寫進報告。
setUp()
和tearDown()
方法可以實現在測試運行前後做一些其他工作,具體使用情況下面提出。
最後這部分展示了一個最簡單的執行測試的方式。unittest.main()
提供了一個命令行接口,當使用命令行運行測試完成後,大致會打印如下信息:
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
使用-v參數將可以顯示更多信息。如下:
test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
上面的例子展示了unittest中最常用的功能特徵,這些足以應付平時的需要。文檔的剩餘部分將從頭開始講解所有unittest的內容。
1.2 命令行
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
你也可以使用文件名代替測試模塊名。指定的文件必須是可導入的。路徑就是去掉“.py”後的文件名,文件名和模塊名之間使用“.”連接。如果你要執行的測試文件不能被作爲模塊導入,也可以直接執行該文件。
通過傳入-v選項,你可以查看更多的執行細節:
python -m unittest -v test_module
當命令行中不傳入任何執行目標時,默認執行Test Discovery中的測試:
python -m unittest
查看所有命令參數:
python -m unittest -h
該功是能在3.2版本中新增的。早期的版本只能運行單獨的測試方法,不能運行模塊和測試類。
1.2.1 命令行選項
unittest支持以下命令行選項:
-b,--buffer
當測試在運行時,輸出的錯誤信息被緩存在內存中,如果測試運行通過,則釋放緩存,如果測試失敗,信息就會被打印出來。
-c,--catch
在測試執行時按下Control-C將會等待當前測試完成並輸入所有測試報告。第二次按下Control-C就會觸發KeyboardInterrupt異常。
-f,--failfast
測試一旦出現錯誤或失敗就停止運行。
--locals
在異常回溯中打印本地變量信息。
命令行選項-b、-c和-f在3.2版本中新增。
命令行選項--locals在3.5版本中新增。
命令行選項也可以在Test Discovery中使用,所有項目中的測試都可以使用。
1.3 Test Discovery
3.2版本中新增。
unittest支持簡單的測試發現功能。爲了兼容test discovery,所有的測試文件必須是能從項目的頂層目錄導入的模塊或包(包括命名空間包),意思就是文件名必須是有效的標識符。
Test Discovery在TestLoader.discover()中實現,但是仍能用於命令行模式。基本使用方法如下:
cd project_directory
python -m unittest discover
注意:python -m unittest雖然作用和python -m unittest discovery一樣。但是如果要傳遞參數給discover的話還是要寫完整的命令。
選項:
-v,--verbose
詳細的輸出
-s,--start-directory 目錄
Discovery尋找的其實目錄(默認.)
-p,--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作爲初始目錄。這樣的話,這個包就會被導入,包的路徑就會被當做初始目錄。
注意:Discovery通過導入的方式來加載測試。一旦test discovery從你提供的初始目錄中發現了所有的測試文件,它就會將路徑轉換成包名導入,比如foo/bar/baz.py會被當做foo.bar.baz進行導入。
如果你有一個在公共的包並且使用test discovery去加載該包的另一個不同副本,那麼就會引發異常,導致discovery退出。
如果你提供了一個起始目錄作爲包名稱而不是提供一個路徑作爲目錄,那麼discovery就會認爲所有位置的文件都是你需要導入的(包括中間路徑),這樣就不會報錯。
測試模塊和包通過load_tests協議以指定的方式加載測試和運行discover。
3.4版本中修改:Test discovery支持命名空間包。
1.4 組織測試代碼
單元測試中最基本的組成是test cases——單一的測試邏輯場景以及正確性校驗。在unittest中,test cases被unittest.TestCae的實例替代。要編寫你自己的測試用例就必須寫一個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()
方法都會執行。
對於測試代碼來講,這樣的工作機制成爲Test Fixture。一個新的TestCase實例被當做Test Fixture創建,用於執行每個測試方法。也就是說,TestCase.setUp
、TestCase.tearDown
和TestCase.__init
__會在每個用例執行前運行一次。
建議使用TestCase去根據測試的特性對其進行分組。對此,unittest提供了一個機制:測試套件(test suit),由unittest的TestSuit類實現。如果測試很多的話,調用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())
你可以將測試用例或者測試套件寫在被測試的代碼中,但是並不建議這麼做。應該將測試部分單獨寫在一個文件,這麼做有以下好處:
- 測試模塊可以單獨在命令行運行
- 測試代碼部分可以更容易的單獨拿出來
- 不會爲了迎合被測試的代碼而隨意修改測試代碼
- 測試代碼會更少的修改
- 被測試代碼重構更容易
- 用C寫的模塊測試的代碼必須是單獨的模塊,那麼爲什麼不保持一直呢?
- 如果測試策略改變了,不需要去修改源碼
1.5 使用舊的測試代碼
一些使用者會發現現存的一些舊的測試代碼並不是TestCase的子類,但是仍需要執行。
基於此,unittest提供了FunctionTestCase類。TestCase的子類可以直接包含這些舊的測試類,並且同樣可以實現用例的setUp()初始化和tearDown()銷燬操作。
例如:
def testSomething():
something = makeSomething()
assert something.name is not None
# ...
此時可以創建一個等效的測試用例如下,包含set-up和tear-down方法:
testcase = unittest.FunctionTestCase(testSomething,
setUp=makeSomethingDB,
tearDown=deleteSomethingDB)
注意:雖然FunctionTestCase可以很快的將普通測試方法轉換成單元測試,但一般不建議這麼做。儘量花時間去構造TestCase的子類作爲測試將會使的以後的代碼重構變得容易。
有時候,已有的測試是使用doctest寫的。如果這樣的話,doctest提供了一個DocTestSuites類,可以自動的將該測試轉換成unittest.TestSuite實例。
1.6 跳過失敗的測試
版本3.1中新增。
Unittest支持跳過單個測試以及整個測試類。此外,還支持將一個測試標記爲“預計失敗”,表示該測試有問題,但是這個失敗不會在TestResult中記錄。
跳過一個測試很簡單,只需使用skip()或者附帶一些條件判斷的skip()。
一般的跳過是這樣的:
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
以下是運行的結果輸出:
test_format (__main__.MyTestCase) ... skipped 'not supported in this library version'
test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping'
test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows'
----------------------------------------------------------------------
Ran 3 tests in 0.005s
OK (skipped=3)
類的跳過和方法一樣:
@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
TestCase.setUp()
也可以設置跳過測試,這在將一些資源設置爲不可用時非常實用。
對於預期的失敗使用exceptedFailture()裝飾器:
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)
如果if的條件成立則跳過測試。
@unittest.skipUnless(condition, reason)
除非條件爲真,否則跳過測試。
@unittest.expectedFailure
標記測試爲失敗,如果在運行時失敗,則不會在結果中統計。
exception unittest.SkipTest(reason)
跳過測試拋出的異常。
通常你可以使用TestCase.skipTest()或者上述裝飾器中的某一個來替代這種拋異常的做法。如果測試被跳過,則不會執行setUp()和tearDown()方法;如果類被跳過,則不會執行setUpClass()和tearDownClass()方法;如果模塊被跳過,則不會執行setUpModule()和tearDownModule()方法。
1.7. 使用subTest()
版本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) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
但是如果不使用subTest()的話,測試會在第一次失敗時就退出,並且錯誤信息不是很明白,比如該例中的i就無法識別:
======================================================================
FAIL: test_even (__main__.NumbersTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
1.8 類和方法
這個章節將深度講解unittest的API部分。
1.8.1. Test Cases
unittest.TestCase(methodName=’runTest’)
類
在unittest
系統中,TestCase
的實例就是邏輯測試單元。該類作爲一個基類,其特性和功能通過創建子類類得以實現。
類內部實現了一些接口,可以讓test runner
驅動測試,以及測試中可以使用一些方法來斷言和報告各種失敗情況。
每一個TestCase
的實例運行時都是運行一個方法:該方法名就是methodName
。在大多數情況下,你都不需要修改methodName
,也不需要重新實現runTest()
方法。
版本3.2中修改內容:TestCase
可以不需要methodName
就進行實例化,這使得TestCase
在交互式平臺上更容易使用。
TestCase
的實例的方法可以分爲3種:一種是用來執行測試的;一種是用來實現條件斷言和失敗報告的;還有一部分是用來收集測試本身的一些信息。
第一種方法大致有:
- setUp()
該方法負責Test Fixture中的準備工作,一般在測試執行前調用;除了AssertionError和SkipTest以外,該方法觸發的其他異常均被是做錯誤處理而不是測試失敗,該方法默認沒有操作。
- tearDown()
該方法在測試方法被執行並且結果記錄後立即被調用。如果測試方法出現異常,子類實現的功能就需要特別的檢查。任何異常,除了AssertionError
和SkipTest
,只要是該方法拋出的異常都會被視爲其他錯誤而不是測試失敗(會在報告中統計)。該方法只有在setUp()執行成功時纔會調用,而跟測試的結果無關。默認該方法沒有操作。
- setUpClass()
只會執行一次。
當類中的測試方法被執行前會被調用的一個類方法。該方法只會在類方法前調用,也就是帶有calssmethod裝飾器並且沒有其他參數的方法。
@classmethod
def setUpClass(cls):
...
版本3.2中新增。
- tearDownClass()
只會執行一次。
當類測試方法被執行完後會被調用的一個類方法。該方法只會在一個類的所有方法執行完成後調用,該方法被調用時,必須有calssmethod裝飾器並且除了類以外沒有其他參數的方法。
@classmethod
def tearDownClass(cls):
...
版本3.2中新增。
- run(result=None)
執行測試,並收集信息作爲結果傳遞給TestResult
對象。如果結果爲空,一個臨時的報告文件就會被調用的defaultTestResult()
方法創建並使用。然後將結果對象返回。
通過簡單的調用TestCase
實例可以達到相同的效果。
版本3.3中修改:早期的run版本中不會返回測試結果,也不會調用實例。
- SkipTest(reason)
當需要跳過一個測試方法時,可以在測試方法或setUp()中調用該方法。
版本3.1中新增。
- **subTest(msg=None,**params)**
把一個封閉的代碼塊當做subTest
執行並返回上下文管理器。msg
和params
是可選的,主要用來顯示信息,以方便測試執行異常時定位問題。
一個測試中可以包含任意個subTest,並且可以多層嵌套。
版本3.4中新增。
- debug()
運行測試但不生成報告。這樣運行測試時會直接拋出異常,可以在調試模式下使用。
1.8.1.1 斷言
TestCase
提供了很多斷言方法,以下是常用的:
assertEqual 如果兩個值相等,則pass
assertNotEqual 如果兩個值不相等,則pass
assertTrue 判斷bool值爲True,則pass
assertFalse 判斷bool值爲False,則pass
assertIsNone 不存在,則pass
assertIsNotNone 存在,則pass
所有的斷言方法都接收一個msg的參數,用來在測試失敗時顯示。注意,msg關鍵字參數只有在assertRaises(),assertRaisesRegex(), assertWarns(), assertWar
nsRegex()方法被當做上下文管理器使用時才能被其使用。
assertEqual(first,second,msg=None)
測試第一個參數和第二個參數是否相等。如果比較不一致,則測試失敗。
此外,如果兩個參數都是同一種類型,且都是某種列表類型的話, addTypeEqualityFunc()會被調用用以顯示更多的錯誤信息。
版本3.1修改:新增自動調用特定對比方法。
版本3.2修改:新增assertMultiLineEqual()方法作爲比較字符串時調用的特定類型方法。
assertNotEqual(first,second,msg=None)
判斷兩個參數不相等。如果相等的話,則測試失敗。
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)
測試第一個參數和第二個參數是否同一個對象。
版本3.1中新增。
assertIsNone(expr,msg=None)
assertIsNotNone(expr,msg=None)
測試expr是否爲None。
版本3.1中新增。
assertIn(first,second,msg=None)
assertNotIn(first,second,msg=None)
測試參數一是否包含在參數二中。
版本3.1中新增。
assertIsInstance(obj,cls,msg=None)
assertNotIsInstance(obj,cls,msg=None)
測試obj是否爲cls的實例(cls可以是一個類或者類組成的元組),檢查具體的對象類型使用assertIs(type(obj),cls)。
版本3.2中新增。
1.8.1.2 檢查異常、警告和日誌信息
還可以使用如下方法檢查異常、警告和日誌信息:
**assertRaises(exception,callable,*args,**kwds)**
assertRaises(exception,msg=None)
測試callable傳入某些參數並調用時引發的異常。如果exception異常被拋出,表示測試通過。如果拋出的是另一種異常或無異常則表示測試失敗。如果需要捕捉一組異常時,exception也可以是一個異常類型組成的元組。
如果參數只傳遞了exception參數,或者也有msg,這樣就可以在with上下文管理器中測試代碼塊而不需要在方法中:
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)**
aseertRaisesRegex(exception,regex,msg=None)
類似於assertRaises(),不過這裏需要匹配regex和捕捉異常的信息是否匹配。regex可以是普通的正則表達式,或是一個包含正則表達式的字符串。例如:
self.assertRaisesRegex(ValueError, "invalid literal for.*XYZ'$",
int, 'XYZ')
或
with self.assertRaisesRegex(ValueError, 'literal'):
int('XYZ')
版本3.1新增:asserRaisesRegexp。
版本3.2修改:重命名爲assertRaiseRegex()。
版本3.3修改:當作爲上下文管理器使用時新增msg參數。
**assertWarns(warning,callable,*args,**kwds)**
assertWarns(warning,mag=None)
當callable被傳入參數調用時,測試其觸發的警告。如果觸發異常則表示測試成功,否則失敗。如果想要測試一組警告,可以在warning出傳入包含警告類的元組。
如果有了warning參數,甚至msg參數,那麼此時可以直接在with代碼塊下測試代碼,而不需要在方法中使用:
with self.assertWarns(SomeWarning):
do_something()
上下文管理器會把捕捉的警告信息存儲在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可以是正則表達式也可以是一個包含正則表達式的字符串:
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被給定,logger將是一個logging.Logger對象,或者logger的名字。默認是root記錄器,會記錄所有信息。
如果給定的話,level可以是數字,表示日誌記錄級別,也可以是字符串格式(比如“error”和logging.ERROR)。默認是logging.INFO。
在with代碼塊中,只要有一條信息和logger和level匹配則表示測試成功,否則失敗。
上下文管理器返回的對象是一個記錄器,記錄了匹配的日誌信息記錄。包含兩個屬性:
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'])
版本3.4中新增。
還有其他執行具體斷言的方法:
assertAlmostEqual(first,second,places=7,msg=None,delta=None)
assertNotAlmostEqual(first,second,places=7,msg=None,delta=None)
測試參數1和參數2是否約等。小數點保留位數爲places(默認7)。
如果delta被提供(替代places),表示參數1和參數2之間的差異必須小於delta。
如果places和delta同時存在時觸發TypeError。
版本3.2修改:assertAlmostEqual()自動識別相似的對象進行比較。如果對象比較一致則assertAlmostEqual()自動失敗。新增delta參數。
assertGreater(first,second,msg=None)
assertGreaterEqual(first,second,msg=None)
assertLess(first,second,msg=None)
assertLessEqual(first,second,msg=None)
正如方法名含義,比較參數1和參數2之間是否存在>、>=、<或者<=關係。
>>> 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修改:重命名爲assertRegex()。
版本3.2新增:assertNotRegex()。
版本3.5新增:assertNotRegexpMatches被棄用。
assertCountEqual(first,second,msg=None)
測試序列first中的元素是否和second中的一致,不考慮排序。如果不同,會生成詳細的錯誤清單。
在比較時,重複的元素不會被忽略。等效於:assertEqual(Counter(list(first)),Counter(list(second))),只適用於序列。
版本3.2新增。
assertEqual()方法爲同類型對象的比較提供了多種具體的比較方法。這些方法大多數都已經在內置方法實現,但是你仍可以使用addTypeEqualityFunction()方法新註冊一個方法:
addTypeEqualityFunc(typeobj,function)
註冊一個被assertEqual()調用的特定類型的方法,用以判斷兩個相同類型的對象(不是子類)是否相等。function必須有2個形參,還有一個msg=None參數,和assertEqual()中使用方法一樣。當2個參數比較第一次出現不相等時就引發self.failureException(msg)異常,這可能會提供一些有用的信息,並在錯誤信息中解釋哪裏不同。
版本3.1新增。
assertEqual()方法會自動的調用下面列表中的方法。但是通常沒有必要直接調用這些方法:
assertMultiLineEqual(first,second,msg=None)
測試多行字符串first是否等同於字符串second。當不相同時,不一致的地方會在錯誤信息中高亮顯示。使用assertEqual()方法比較字符串時,這個方法會被默認調用。
版本3.1新增。
assertSequenceEqual(first,second,msg=None,seq_type=None)
測試兩個序列是否相等。如果seq_type被提供,參數1和參數2都必須是seq_type的實例,否則引發異常。如果兩個序列不等,會在錯誤信息中顯示不同。
版本3.1新增。
assertListEqual(first,second,msg=None)
assertTupleEqual(first,second,msg=None)
測試列表或者元組是否相等。如果不同,會打印錯誤信息並顯示不同之處。當參數類型錯誤時仍會報錯。這些方法通常是在比較列表和元組時被assertEqual()默認調用。
assertSetEqual(first,second,msg=None)
測試集合是否相等。如果不同,會打印錯誤信息並顯示不同之處。該方法通常是在比較集合和frozensets時被assertEqual()默認調用。
版本3.1新增。
assertDictEqual(first,second,msg=None)
測試字典是否相等。如果不同,會打印錯誤信息並顯示不同之處。該方法通常是在比較字典時被assertEqual()默認調用。
版本3.1新增。
最後,TestCase提供了一下方法和屬性:
fail(msg=None)
當某個測試失敗時,可以將msg提供給錯誤信息。
failureException
該類屬性提供了測試方法拋出的異常。如果測試框架需要一個特定的異常,比如攜帶更多的信息,就必須在子類中實現該異常。該屬性的初始值爲AssertionError。
longMessage
這個類屬性決定了當一個自定義失敗信息被當做msg參數傳遞給assertXYY等方法時該做什麼。默認值是True。這種情況下,自定義的信息被添加到標準失敗信息後面。當值爲False時,自定義信息會覆蓋標準信息。
在每個測試方法中,可以重寫該屬性:調用assert方法之前,分配實例屬性self.longMessage的值爲True或False。
每次測試運行前該設置都會被重置。
版本3.1新增。
maxDiff
該屬性決定了assert方法結果中差異信息的顯示長度。默認有80*8個字符。受此影響的方法有assertSequenceEqual()(包括所有序列的比較方法),assertDictEqual()和assertmultiLineEqual()。
如果maxDiff設置爲None時,表示沒有最大長度。
版本3.2新增。
測試框架可以使用如下方法在測試中收集信息:
countTestCases()
通過測試對象返回測試的數量。對於TestCase的實例來說,這個值是1.
defaultTestResult()
返回一個測試報告類的實例,用於test case類(如果沒有提供其他報告實例給run()方法)。
id()
返回用例的id。通常是測試方法的全名,包括模塊名和類名。
shortDescription()
返回測試的描述信息,如果沒有則返回None。該方法默認會返回方法docstring信息的第一行,如果不可用則返回None。
版本3.1修改:在3.1中添加了測試的名稱在描述中。這個特性與unittest的擴展存在衝突,於是在python3.2中增加測試名稱被轉移到了TextTestResult。
**addCleanup(function,*args,**kwargs)**
在tearDown()方法之後新增調用function方法去清理測試時使用的資源。方法會根據添加的反序進行調用(LIFO)。當他們被調用時,可以接收任何傳遞給addCleanUp()的參數。
如果setUp()失敗,那麼tearDown()就不會執行。但是cleanup方法仍會被執行。
版本3.1新增。
doCleanups()
該方法會在tearDown()之後被調用,或者當setUp()失敗時。
該方法負責調用所有添加在addCleanup()中的清理方法。如果你需要清理在tearDown()中被調用的方法,那麼你需要手動調用doCleanup()方法。
doCleanup()將方法從清理方法棧中取出,所以它可以在任何時間調用。
版本3.1新增。
class unittest.FunctionTestCase(testFunc,setUp=None,tearDown=None,description=None)
該類實現了TestCase接口即允許test runner操縱測試,但是沒有提供測試代碼檢查和報告錯誤的方法。這通常被用來將剩餘的測試代碼創建爲測試用例,並且可以集成到unittest測試框架中。
1.8.2. 棄用的方法
由於一些歷史的原因,一些TestCase的方法別名被棄用。以下列表是這些方法的正確名字和被棄用的名字:
1.9 組織用例
class unittest.TestSuite(tests=())
這個類代表一個測試用例或測試套件的集合。該類描述了一個可以被test runner執行的接口,通過它可以執行任何測試。運行一個TestSuite就相當於將測試套件迭代,然後執行每一個測試。
如果tests被給與,那麼它必須是一個可迭代對象,用來創建套件。稍後會介紹一些添加測試或測試套件到測試集合中的一些方法。
TestSuite對象和TestCase對象比較類似,除了不會執行測試以外。相反,TestSuite經常用來吧測試用例組織在一起,使測試可以一起執行。一些方法可以用來將測試添加到TestSuite中去:
addTest(test)
添加一個TestCase或TestSuite到套件中。
addTests(tests)
把TestCase和TestSuite中給的所有的測試實例添加到套件中。
等價於迭代所有的測試,然後使用addTest()逐個添加。
TestSuite和TestCase都有如下方法:
run(result)
運行測試套件,並收集結果信息傳遞給測試報告對象。注意不同於TestCase.run(),TestSuite.run()需要傳遞result對象。
debug()
運行測試套件但不收集測試結果。允許異常拋出並傳遞給上層調用者。經常用在調試模式下。
countTestCases()
返回測試數量,包括所有的單獨測試和部分測試套件。
iter()
用例通常通過TestSuite的迭代訪問。子類可以通過重寫__iter__()來提供用例。注意這個方法可能會在一個套件被調用好多次(比如計數或者比較),所以在TestSuite.run()之前每次迭代返回的用例都是一樣的。在TestSuite.run()之後,調用者就不再依賴該方法返回的用例,除非調用者使用子類重寫了TestSuite._removeTestAtIndex()以保存用例的引用。
1.9.1 加載和執行用例
class unittest.TestLoader
TestLoader經常用來從類和模塊中提取創建測試套件。一般情況下TestLoader不需要實例化,unittest內部提供了一個實例可以當做unittest.defaultTestLoader使用。無論使用子類還是實例,都允許自定義一些配置屬性。
TestLoader對象具有以下屬性:
errors
TestLoader
對象具有以下方法:
loadTestsFromTestCase(testCaseClass)
從TestCase派生類testCaseClass中加載所有測試用例並返回測試套件。
getTestCaseNames()會從每一個方法中創建Test Case實例,這些方法默認都是test開頭命名。如果getTestCaseNames()沒有返回任何方法,但是runTest()方法被實現了,那麼一個test case就會被創建以替代該方法。
loadTestsFromModule(module,pattern=None)
從模塊中加載所有測試用例,返回一個測試套件。該方法會從module中查找TestCase的派生類併爲這些類裏面的測試方法創建實例。
注意:當使用TestCase派生類時可以共享test fixtures和一些輔助方法。該方法不支持基類中的測試方法。但是這麼做是有好處的,比如當子類的fixtures不一樣時。
如果一個模塊提供了load_tests方法,它會在加載測試時被調用。這樣可以自定義模塊的測試加載方式。這就是load_tests協議。pattern作爲第三個參數被傳遞給load_tests。
loadTestsFromName(name,module=None)
從一個字符串中加載測試並返回測試套件。
字符串說明符name是一個虛名(測試方法名的字符串格式),可以適用於模塊、測試類、test case類中的測試方法、TestSuite實例或者一個返回TestCase或TestSuite的可調用的對象。並且這樣的話,在一個測試用例類中的方法會被當做‘測試用例類中給的測試方法’而不是‘可調用對象’。
比如,你有一個SampleTests模塊,裏面有一個TestCase的派生類SampleTestCase,類中有3個方法(test_one(),test_two(),test_three()),使用說明符“SampleTests.SampleTestCase”就會返回一個測試套件,裏面包含所有3個測試方法。如果使用“SampleTests.SampleTestCase.test_two”就會返回一個測試套件,裏面只包含test_two()測試方法。如果說明符中包含的模塊或包沒有事先導入,那麼在使用時會被順帶導入。
loadTestsFromNames(names,module=None)
使用方法和loadTestsFromName(name,module=None)一樣,不同的是它可以接收一個說明符列表而不是一個,返回一個測試套件,包含所有說明符中的所有測試用例。
getTestCaseName(testCaseClass)
返回一個有序的包含在TestCaseClass中的方法名列表。可以看做TestCase的子類。
discover(start_dir,pattern=’test*.py’,top_level_dir=None)
從指定的start_dir(起始目錄)遞歸查找所有子目錄下的測試模塊,並返回一個TestSuite對象。只有符合pattern模式匹配的測試文件纔會被加載。模塊名稱必須有效才能被加載。
頂級項目中的所有模塊必須是可導入的。如果start_dir不是頂級路徑,那麼頂級路徑必須單獨指出。
如果導入一個模塊失敗,比如由於語法錯誤,會被記錄爲一個單獨的錯誤,然後discovery會繼續。如果導入失敗是由於設置了SkipTest,那麼就會被記錄爲忽略測試。
當一個包(包含__init__.py文件的目錄)被發現時,這個包會被load_tests方法檢查。通過package.load_tests(loader,tests,pattern)方式調用。Test Discovery始終確保包在調用時只進行一次測試檢查,即使load_tests方法自己調用了loader.discover。
如果load_tests方法存在,那麼discovery就不再遞歸,load_tests會確保加載完所有的當前包下的測試。
pattern沒有被特意存儲爲loader的屬性,這樣包就可以自行查找。top_level_dir被存儲,所以load_tests就不需要再傳參給loader.discover()。
start_dir可以是一個模塊名,也可以是一個目錄。
以下這些TestLoader的屬性可以在子類和實例中配置:
testMethodPrefix
一種字符串,放在方法的名字前面時,該方法會被當做測試方法。默認一般是‘test’。
在getTestCaseNames()和所有的loadTestsFrom*()方法中都有效。
sortTestMethodUsing
在getTestCaseNames()和所有的loadTestFrom*()方法中對測試方法進行排序時對測試方法名進行比較的函數。
suiteClass
從一個測試序列中構造出一個測試套件的可調用對象。生成的對象沒有方法。默認值是TestSuite類。
對所有的loadTestsFrom*()方法有效。
class unittest.TestResult
該類被用來整理測試報告。
一個TestResult對象保存了一系列測試的結果。TestCase和TestSuite類確保了結果會被正確的記錄。測試人員不需要擔心測試的輸出問題。
基於unittest的測試框架通過運行一系列測試生成的TestResult對象來實現測試報告的目的。TestRunner.run()方法會返回一個TestResult對象來達到這個目的。
TestResult對象具有以下屬性,在檢查測試結果時很有用:
errors
一個錯誤列表,包含了TestCase實例和異常回溯的二元組。每個元組都表示測試中出現的異常。
failures
一個列表,包含了TestCase實例和異常回溯的二元組。每個元組都表示那些使用TestCase.assert*()導致失敗的測試。
skipped
一個列表,包含了TestCase實例和忽略測試原因的二元組。
expectedFailures
一個列表,包含了TestCase實例和異常回溯的二元組。每個元組都代表一個預期的異常。
unexpectedSuccesses
一個列表,包含了被標記爲預期失敗的TestCase實例,但是卻成功了。
shouldStop
當一個測試執行應該通過stop()終止時,值設置爲True。
testsRun
到目前爲止的總運行次數。
buffer
設置爲True時,sys.stdout和sys.stderr會在startTest()和stopTest()方法調用之間被緩存。如果測試失敗或錯誤,那麼輸出的內容就是真實的sys.stdout或者sys.stderr。其他輸出的內容會追加在失敗/錯誤信息的後面。
failfast
一旦設置爲 True,測試在第一次出現失敗或錯誤時就會調用stop()方法終止。
tb_locals
如果設置爲True,本地變量信息會打印在異常回溯信息中。
wasSuccessful()
如果所有的測試都運行通過,則返回True,否則返回False。
stop()
該方法被調用時通過設置shouldStop屬性爲True,從而終止運行測試。TestRunner對象一旦遇到該方法就不會繼續執行測試而直接返回。
比如,這個功能可以被TextTestRunner類用來終止測試框架,當用戶在鍵盤輸入中斷信號時。在交互式環境下TestRunner可以使用類似的操作。
以下這些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)
當測試通過時調用。默認無實現。
addSkip(test,reason)
當測試test被跳過時調用。reason是測試跳過的原因。
默認實現是給實例的skipped屬性添加了元組(test,reason)。
addExpectedFailure(test,err)
當測試失敗時調用,但是是被expectedFailure()裝飾器裝飾的測試。
默認的實現是給實例的expectedFailures屬性添加一個元組(test,formatted_err),其中formatted_err的格式化信息來自err。
addUnexpectedSuccess(test)
在被expectedFailure()裝飾的測試test執行成功時調用。默認實現是給實例的unexpectedSuccess屬性添加test。
addSubTest(test,subtest,outcome)
當subtest結束時調用。test是測試方法對應的測試用例。subtest是一個自定義的TestCase實例。
如果outcome爲None,subtest成功。否則,測試失敗並拋出異常,outcome爲sys.exc_info返回的元組:(tyep,calue,traceback)。
默認的實現是,如果測試成功則不做任何操作,如果失敗則記錄subtest失敗爲正常的失敗。
class unittest.TextTestResult(stream,descriptions,verbosity)
一個TestResult實現的功能,被TextTestRunner使用。
unittest.defaultTestLoader
TestLoader類的實例,主要爲了共享。如果不需要自定義的TestLoader,這個實例就會被使用,而不會重新創建實例。
class unittest.TextTestRunner(stream=None,description=True,verbosity=1,failfast=False,buffer=False,resultclass=None,warning=None,*,tb_locals=False)
一個基礎的測試執行器,實現了將結果輸出爲流的功能。如果stream爲None,默認的,sys.stderr會被作爲輸出流。這個類有一些可配置參數,但非常簡單。運行測試套件的圖形程序應該提供一些功能,這些功能可以接收**kwargs參數作爲修改runners的接口,當在unittest中使用時。
默認情況下該執行器會顯示 DeprecationWarning, PendingDeprecationWarnin
g, ResourceWarning和ImportWarning,即使他們默認是忽略的。棄用警告被deprecated unittest方法引發,如果警告的過濾器是‘default’或者‘always’時,該警告只會在每個模塊中出現一次,爲了避免出現太多的警告信息。可以通過python的 -Wd或-Wa選項重新設置(查看Warning控制)。
**_makeResult()**
該方法返回一個TestResult的實例,通過run()方法。它不能被直接調用,但是可以通過在子類中重寫類實現自定義TestResult。
_makeResult()實例化類或者可調用的傳遞給TextTestRunner構造函數的參數resultclass。如果沒有提供resultclass的話,默認值是TextTestResult。resultclass通過以下參數實例化:
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)
一種命令行模式下,加載和執行測試用例,這樣做的主要作用是可以很簡便的執行測試模塊。該方法最簡單的使用方式是在腳本的最後添加如下語句:
if __name__ == '__main__':
unittest.main()
通過傳入verbosity參數運行測試,可以得到更多的詳細信息:
if __name__ == '__main__':
unittest.main(verbosity=2)
參數defaultTest 是一個待執行的單獨測試的名或者一序列的測試名(可迭代對象),如果沒有通過argv參數傳入指定的測試的話。如果defaultTest沒有指定值或者值爲None,並且參數argv沒有傳入值,那麼所有在模塊中發現的測試都會被執行。
參數argv可以傳遞一系列選項給程序,第一個參數默認是程序名。如果沒有指定值或者值爲None,那麼默認會使用sys.argv的值。
參數testRunner可以是一個test runner類,也可以是一個已經創建好的test runner類的實例。通常情況下,main函數在測試執行成功或失敗時調用sys.exit()作爲退出。
testLoader參數必須是TestLoader的實例,默認值爲defaultTestLoader。
main方法支持在交互式環境下使用,通過傳入參數exit=False。以下顯示沒有調用sys.exit()方法的標準結果輸出:
>>> from unittest import main
>>> main(module='test_module', exit=False)
failfast,catchbreak和buffer參數在命令行模式下,傳入同樣的選項的效果是一樣。
warnings參數是一個等級過濾器,在測試執行時會用到。如果沒有指定值,且命令行中傳遞了一個-w選項,那麼該值會被鎖定爲None,否則該值會被設置爲‘default’。
調用main方法實際上返回了一個TestProgram類的實例。保存了測試執行的結果,作爲result的屬性。
1.9.2. load_tests*協議*
版本3.2新增。
在正常執行測試時或者使用test discovery(通過實現load_tests方法),可以自定義從模塊和包中加載測試用例的方式。
如果一個模塊定義了load_tests方法,它會通過使用以下參數被TestLoader.loadTestsFromModule()調用:
load_tests(loader, standard_tests, pattern)
pattern參數直接從loadTestFromModule傳來,默認是None。
該方法會返回一個TestSuite對象。
loader是TestLoader的實例,用來加載實例的。standard_tests是默認要從模塊中加載的測試。從模塊中只加載少數測試或者從測試集中去除部分測試是很常見的。第三個參數pattern就是用來加載部分測試用例的。
一個典型的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
如果discovery啓動的目錄下有其他包,那麼該包的__init__.py會被檢查,通過命令行或是調用TestLoader.discover()。如果該方法不存在,那麼discovery就會遍歷這個包目錄。否則,discovery就會被load_tests方法代替,通過以下參數調用:
load_tests(loader, standard_tests, pattern)
該方法會返回一個包含該包下所有測試的測試套件。standard_tests只包含從__init__.py中找到的測試。
由於傳入了pattern參數給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
2.0.類和模塊的Fixtures
類和模塊級別的Fixtures已經在TestSuite中被實現。當一個新的類中發現有測試時,前面的類的tearDownClass()就會被執行,然後新類的setUpClass()就會被執行。
同樣的當一個模塊中的測試執行完後就會執行tearDownModule()方法,而在新的模塊中執行測試前會執行setUpModule()。
在所有的類或模塊中的所有測試執行完成後,就會執行tearDownClass或者tearDownModule。
注意,fixtures和一些‘有潛力’的特性(比如測試並行)一起使用的話不是很好。因爲它會打破測試之間的隔離。需要小心的使用。
unittest的test loader創建了默認的測試執行順序,用以組織相同模塊或類下的所有測試用例。這樣的話,在每個類/模塊之前都會調用一次setUpClass/setUpModule。如果你把不同模塊和類的測試混在了一起,那麼在執行測試時,fixture方法就可能被調用多次。
fixtures並不是爲了和無序的測試套件一起工作。事實上框架中就有一個BaseTestSuite並不支持fixtures。
當fixture方法拋出異常時,當前執行的測試也會被記錄爲錯誤。因爲沒有對應的測試實例_ErrorHolder對象(和TestCase相同的接口)創建給對應的錯誤。如果你只是使用unittest的test runner功能,那麼這個細節無關緊要,但如果你是一個框架設計者的話,那這個就有關係了。
2.0.1 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
異常的話,那麼該類會被記錄爲跳過測試而不是錯誤。
2.0.2 setUpModule 和 tearDownModule
這可以被當做方法實現:
def setUpModule():
createConnection()
def tearDownModule():
closeConnection()
如果setUpModule報異常的話,那麼該模塊的測試用例和tearDownModule都不會執行。被設置跳過的類也不會執行setUpModule和tearDownModule。如果異常是一個SkipTest異常的話,那麼該模塊會被記錄爲跳過測試而不是錯誤。
2.1 信號處理
對unittest
來說-c/--catch命令行選項,或者catchbreak
參數對於unittest.main()
,提供了更多的友好的處理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)
記錄一個TestResult對象給control-C處理程序。記錄的result存儲了一個弱引用,所以它不會阻止結果被回收成垃圾。
記錄一個TestResult對象任何副作用,如果control-C處理程序沒有啓用的話,那麼測試框架就會無條件的記錄所有的他們創建的結果,而不管處理程序是否棄用。
- unittest.removeResult(result)
移除記錄的結果。一旦結果被移除,那麼stop()就不會在result對象上調用作爲control-C的響應。
- unittest.removeHandler(function=None)
當無參數調用時,該方法會移除control-C處理程序,如果它已經被安裝。該方法還可以作爲一個測試裝飾器,當測試執行時暫時的去除處理程序。
@unittest.removeHandler
def test_signal_handling(self):
...