1. 程式人生 > 實用技巧 >unittest單元測試框架教程6-unittest.TestCase類詳解

unittest單元測試框架教程6-unittest.TestCase類詳解

unittest.TestCase(methodName ='runTest')

TestCase類的例項,作為編寫的測試類的基類,具體測試由具體的子類(就是我們寫的測試類)實現。此類實現測試執行程式所需的介面,以使其能夠驅動測試,以及測試程式碼可用於檢查和報告各種失敗的方法。

每個TestCase例項(就是我們寫的測試類)將執行一個基本方法:我們編寫的測試方法TestCase例項提供了三組方法:一組用於執行測試,另一組由測試實現用於檢查條件和報告故障,還有一些查詢方法允許收集有關測試本身的資訊。

第一組的方法是:

setUp()

在呼叫測試方法之前立即呼叫該方法。除了AssertionError

SkipTest之外,此方法引發的任何異常都將被視為錯誤而不是測試失敗。預設實現不執行任何操作。

tearDown()

呼叫測試方法並記錄結果後立即呼叫的方法。即使測試方法引發了異常,也將呼叫此方法,因此子類中的實現可能需要特別注意檢查內部狀態。除了AssertionErrorSkipTest之外,此方法引發的任何異常都將被視為錯誤(errors)而不是測試失敗(Failures)setUp()無論測試方法的結果如何,僅在成功的情況下才呼叫此方法(wasSuccessful()==True)。預設實現不執行任何操作。

setUpClass()

在執行單個類中的測試之前呼叫的類方法。setUpClass

以類作為唯一引數呼叫,並且必須修飾為classmethod()

@classmethod
def setUpClass(cls):
    ...

tearDownClass()

與setUpClass原理一致

run(result = None)

執行測試,將結果收集到TestResult作為result傳遞物件中如果省略resultNone,則建立一個臨時結果物件(通過呼叫該defaultTestResult()方法)並使用它。結果物件返回給run()的呼叫者。

通過簡單地呼叫TestCase例項

result = TestAdd('test_add1').run()
result = TestAdd('test_chengfa').run()
print(result)

會生成一個測試結果

<unittest.result.TestResult run=1 errors=0 failures=1>

skipTest(reason)

@unittest.skip(reason)

跳過被此裝飾器裝飾的測試。reason為測試被跳過的原因。

@unittest.skipIf(condition,reason)

condition為真時,跳過被裝飾的測試。

@unittest.skipUnless(condition,reason)

跳過被裝飾的測試,除非condition為真。

@unittest.expectedFailure

把測試標記為預計失敗。如果測試不通過,會被認為測試成功;如果測試通過了,則被認為是測試失敗。

exceptionunittest.SkipTest(reason)

引發此異常以跳過一個測試。

通常來說,你可以使用TestCase.skipTest()或其中一個跳過測試的裝飾器實現跳過測試的功能,而不是直接引發此異常。

被跳過的測試的setUp()tearDown()不會被執行。被跳過的類的setUpClass()tearDownClass()不會被執行。被跳過的模組的setUpModule()tearDownModule()不會被執行。

    def test_chengfa(self):
        '''測試乘法程式'''
        self.skipTest('暫不測試')
        ...

加入後執行,就會跳過

1
2
1
2
1
2
<unittest.runner.TextTestResult run=3 errors=0 failures=0>
.s.
----------------------------------------------------------------------
Ran 3 tests in 0.041s

OK (skipped=1)

subTest(msg = None** params)

此引數在***已經詳細介紹,不重複介紹

debug()

執行測試而不收集結果。這樣可以將測試引發的異常傳播到呼叫方,並可以用於支援在偵錯程式下執行測試。

result = TestAdd('test_add1').debug()
result = TestAdd('test_chengfa').debug()
print(result)

執行後會詳細的輸出錯誤資訊,便於定位

1
2
Traceback (most recent call last):
  File "D:\PycharmProjects\untitled\testrunner.py", line 13, in <module>
    result = TestAdd('test_chengfa').debug()
  File "C:\Users\MZM\AppData\Local\Programs\Python\Python37-32\lib\unittest\case.py", line 681, in debug
    getattr(self, self._testMethodName)()
  File "D:\PycharmProjects\untitled\testmath.py", line 53, in test_chengfa
    self.assertEqual(resp['data'], self.a * self.b)
  File "C:\Users\MZM\AppData\Local\Programs\Python\Python37-32\lib\unittest\case.py", line 852, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\Users\MZM\AppData\Local\Programs\Python\Python37-32\lib\unittest\case.py", line 845, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: 3 != 2

第二組方法提供了一些斷言方法來檢查並報告故障。

assertRaises(exceptioncallable* args** kwds)

測試除法分母為0報異常

    def test_chufa(self):
        '''測試除法程式'''
        self.b = 0
        headers = {
            'Content-Type': "application/json",
        }
        reqdata = {'a':self.a,'b':self.b}
        resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/chengfa/', verify=False, headers=headers, json=reqdata)
        resp = json.loads(resp.text)
        self.assertEqual(resp['status'],1)
        self.assertEqual(resp['message'], '請求成功')
        self.assertEqual(resp['data'], self.a / self.b)
1
0
<unittest.runner.TextTestResult run=1 errors=1 failures=0>
E
======================================================================
ERROR: test_chufa (testmath.TestAdd)
測試除法程式
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PycharmProjects\untitled\testmath.py", line 67, in test_chufa
    self.assertEqual(resp['data'], self.a / self.b)
ZeroDivisionError: division by zero

----------------------------------------------------------------------
Ran 1 test in 0.029s

FAILED (errors=1)

加入assertraise斷言後不會報錯

        with self.assertRaises(ZeroDivisionError):
            self.assertEqual(resp['data'], self.a / self.b)
1
0
<unittest.runner.TextTestResult run=1 errors=0 failures=0>
.
----------------------------------------------------------------------
Ran 1 test in 0.030s

OK

assertRaisesRegex(exceptionregexcallable* args** kwds )

也是一樣的操作

with self.assertRaisesRegex(ZeroDivisionError, 'ok'):
self.assertEqual(resp['data'], self.a / self.b)
F
======================================================================
FAIL: test_chufa (testmath.TestAdd)
測試除法程式
----------------------------------------------------------------------
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\PycharmProjects\untitled\testmath.py", line 68, in test_chufa
    self.assertEqual(resp['data'], self.a / self.b)
AssertionError: "ok" does not match "division by zero"

----------------------------------------------------------------------
Ran 1 test in 0.030s

FAILED (failures=1)
1
0
<unittest.runner.TextTestResult run=1 errors=0 failures=1>
with self.assertRaisesRegex(ZeroDivisionError, 'by'):
    self.assertEqual(resp['data'], self.a / self.b)
1
0
<unittest.runner.TextTestResult run=1 errors=0 failures=0>
.
----------------------------------------------------------------------
Ran 1 test in 0.028s

OK

用於執行更具體檢查的方法

assertAlmostEqual(a, b)

舉個例子self.assertAlmostEqual(1.00000002,1.00000001)第八位不報錯,self.assertAlmostEqual(1.0000001,1.0000002)第七位就會報錯,因為預設比的是第七位之前

self.assertAlmostEqual(1.02,1.01,1)不報錯self.assertAlmostEqual(1.02,1.01,2)報錯,因為預設比的是第二位之前

place引數表示第n位前都相等,之後無所謂

assertRegex(textregexmsg = None)

測試正則表示式搜尋是否匹配(或不匹配)text

assertCountEqual(firstsecondmsg = None)

測試第一個序列是否包含與第二個相同的元素,而不管它們的順序如何。否則,將生成一條錯誤訊息,列出序列之間的差異。

下表總結了自動比較使用的特定型別方法的列表

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

failmsg = None

failureException

此類屬性給出了測試方法引發的異常。如果測試框架需要使用專門的異常(可能帶有其他資訊),則它必須將該異常子類化,以便與框架“公平競爭”。此屬性的初始值為AssertionError

使用msgNone錯誤訊息無條件地指示測試失敗

程式碼中加入

        if resp['status'] == 0:
            raise self.failureException(ConnectionError)

並修改測試程式使其返回status:0

1
2
<unittest.runner.TextTestResult run=1 errors=0 failures=1>
F
======================================================================
FAIL: test_add1 (testmath.TestAdd)
測試加法程式
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PycharmProjects\untitled\testmath.py", line 26, in test_add1
    raise self.failureException(ConnectionError)
AssertionError: <class 'ConnectionError'>

----------------------------------------------------------------------
Ran 1 test in 0.035s

FAILED (failures=1)

測試框架可以使用以下方法來收集有關測試的資訊:

countTestCases()

返回此測試物件表示的測試數量。

print(TestAdd('test_chengfa').countTestCases())
loader = unittest.TestLoader()
suite = loader.loadTestsFromTestCase(TestAdd)
print(suite.countTestCases())
suite.addTest(TestAdd('test_chengfa'))
print(suite.countTestCases())

1
4
5

id()

返回標識特定測試用例的字串。這通常是測試方法的全名,包括模組和類名。

print(TestAdd('test_chengfa').id())

返回testmath.TestAdd.test_chengfa

shortDescription()

返回測試的描述,或者None沒有提供描述。此方法的預設實現返回測試方法docstring的第一行(如果有),或None

doCleanups()

tearDown()或在setUp()引發異常之後無條件呼叫此方法

如果使setup方法報錯

def setUp(self):
self.file = open('testtext.txt','w+',encoding='utf-8')
self.file.write('測試開始')
self.a = 1/0
self.b = 2/0

def tearDown(self):
print(self.a)
print(self.b)
self.file.write('測試結束')
self.file.close()


...

runner = unittest.TextTestRunner()
suite = unittest.TestSuite()
suite.addTest(TestAdd('test_add1'))
result = runner.run(suite)

出現錯誤,且不會執行tearDown

E
======================================================================
ERROR: test_add1 (testmath.TestAdd)
測試加法程式
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PycharmProjects\untitled\testmath.py", line 11, in setUp
    self.a = 1/0
ZeroDivisionError: division by zero

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)
<unittest.runner.TextTestResult run=1 errors=1 failures=0>

加入doCleanups()後

    def doCleanups(self):
        self.file.write('測試結束')
        self.file.close()

就會將"測試結束"寫入檔案了