33.Python的單元測試工具—— unittest(高階)
這篇部落格詳細介紹Python的unittest模組內部的類及方法的更多內容。
unittest.TestCase類
class unittest.TestCase(methodName='runTest')
這個類的例項表示一個測試用例,預設的methodName是runTest
,即最簡單的測試用例類的定義只包含runTest
方法的定義。如果同時定義了runTest
方法和以test開頭命名的方法,會忽略runTest
方法。如果要指定執行某些方法,可以這樣:
suite = unittest.TestSuite()
suite.addTest(Test('test_al'))
unittest.TextTestRunner(verbosity=2 ).run(suite)
TestCase類中定義的方法分為三大類:測試執行;結果檢查及錯誤上報;查詢測試用例資訊。
測試執行
setUp()
在執行每個測試用例之前被執行,任何異常(除了unittest.SkipTest
和AssertionError
異常以外)都會當做是error而不是failure,且會終止當前測試用例的執行。
tearDown()
執行了setUp()
方法後,不論測試用例執行是否成功,都執行tearDown()
方法。如果tearDown()的程式碼有異常(除了unittest.SkipTest
和AssertionError
異常以外),會多算一個error。
setUpClass(cls)與tearDownClass(cls)
測試用例們被執行前、後執行的方法,定義時必須加上classmethod裝飾符,比如:
import unittest
class MyTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
print 'set up class ran'
def setUp(self):
print 'set up test case ran'
def test_equal(self):
self.assertEqual(1 , 1, '1 not equals 1')
def test_true(self):
self.assertTrue('LOO'.isupper(), 'LOO not upper')
def tearDown(self):
print 'tear down test case ran'
@classmethod
def tearDownClass(cls):
print 'tear down class ran'
測試結果如下:
$ python -m unittest unit
set up class ran
set up test case ran
tear down test case ran
.set up test case ran
tear down test case ran
.tear down class ran
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
skipTest(reason)
在某個測試用例函式的定義中呼叫skipTest(reason)會忽略這個測試用例的執行,在setUp()方法中呼叫,會忽略所有測試用例的執行。其實skipTest(reason)函式丟擲的就是unittest.SkipTest異常。
run(result=None)
執行一個測試用例,將測試結果收集到result變數中,測試結果不返回給呼叫者。如果result引數的值為None,則測試結果在下面提到的defaultTestResult()方法的返回值中。比如:
import unittest
class Sample(unittest.TestCase):
def test_a(self):
assert 1 == 2, 'test print Errors'
print 'test_a'
if __name__ == '__main__':
r = unittest.TestResult()
Sample('test_a').run(result=r)
print r.__dict__
執行結果如下:
debug()
與run方法將測試結果儲存到result變數中不同,debug方法執行測試用例將異常資訊上報給呼叫者。
結果檢查及錯誤上報
Method | Checks that |
---|---|
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 |
assertIsNot(a, b) | a is not b |
assertIsNone(x) | x is None |
assertIsNotNone(x) | x is not None |
assertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
assertIsInstance(a, b) | isinstance(a, b) |
assertNotIsInstance(a, b) | not isinstance(a, b) |
assertAlmostEqual(a, b) | round(a-b, 7) == 0 |
assertNotAlmostEqual(a, b) | round(a-b, 7) != 0 |
assertGreater(a, b) | a > b |
assertGreaterEqual(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertRegexpMatches(s, r) | r.search(s) |
assertNotRegexpMatches(s, r) | not r.search(s) |
assertItemsEqual(a, b) | sorted(a) == sorted(b) and works with unhashable objs |
上面所有的方法都支援新增第三個字串引數,用於出錯時的資訊展示。
assertIsInstance(a, b)與assertNotIsInstance(a, b)
assertIsInstance(a, b)和assertNotIsInstance(a, b)中的型別b,既可以是一個型別,也可以是型別組成的元組。
assertAlmostEqual與assertNotAlmostEqual
assertAlmostEqual(first, second, places=7, msg=None, delta=None)
assertNotAlmostEqual(first, second, places=7, msg=None, delta=None)
判斷兩個值是否約等於或者不約等於,places表示小數點後精確的位數。比如,如果精確到小數點後兩位,1.112和1.113是相等的:
import unittest
class Test(unittest.TestCase):
def test_al(self):
self.assertAlmostEqual(1.111, 1.112, places=2)
如果精確到後3位,則是不相等的:
如果提供了delta引數,則不能同時提供places引數(這時比較兩個變數的差值<=delta或者>delta)。
assertItemsEqual(a, b)
比較a和b的元素相同,且不忽略重複的元素,比如[2, 3, 1]與[3, 2, 1]是items equal的,但是[2, 2, 1]和[2, 1]不是item equal的。
addTypeEqualityFunc(typeobj, function)
如果有自定義的類,這個函式可以為自定義的類提供相等性檢查方法,比如:
import unittest
class Mars(object):
def __init__(self):
self.value = 2
def change_to(self, value):
self.value = value
def get_value(self):
return self.value
def looFunc(first, second, msg=None):
if first.get_value() != second.get_value():
raise unittest.TestCase.failureException(msg)
class MarsTest(unittest.TestCase):
def __init__(self, methodName):
super(MarsTest, self).__init__(methodName)
self.addTypeEqualityFunc(Mars, looFunc)
def test_my(self):
a = Mars()
b = Mars()
b.change_to(6)
self.assertEqual(a, b, 'a not equal b')
執行結果如下:
檢查程式中應該丟擲的異常資訊
Method | Checks that |
---|---|
assertRaises(exc, fun, *args, **kwds) | fun(*args, **kwds) raises exc |
assertRaisesRegexp(exc, r, fun, *args,**kwds) | fun(*args, **kwds) raises exc and the message matches regex r |
檢查異常的方法是沒有錯誤資訊提示引數的。如果期望的異常有多個,也可以給exc引數一個多個異常組成的元組,比如:
import unittest, random
def raise_exec1():
raise Exception('mars')
def raise_exec2():
raise random.choice([NameError('[3] failed'), TypeError('[1]')])
class MyTestCase(unittest.TestCase):
def test_exec(self):
self.assertRaises(Exception, raise_exec1)
def test_exec2(self):
self.assertRaisesRegexp((NameError, TypeError), r'\[\d\]\w*', raise_exec2)
如果只提供exc引數也是可以的,這時返回一個上線文管理器,可以配合with語句把要呼叫的函式放在上線文管理器中呼叫,並且unittest將被呼叫函式返回的異常賦值給上下文管理器的返回值的exception屬性,比如:
import unittest
class marsException(Exception):
error_code = 3
def raise_exec1():
raise marsException('mars')
class MyTestCase(unittest.TestCase):
def test_exec1(self):
with self.assertRaises(marsException) as cm:
raise_exec1()
self.assertEqual(cm.exception.error_code, 3,
'Error code error')
測試結果如下:
$ python -m unittest -v unit
test_exec1 (unit.MyTestCase) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
fail(msg=None)
無條件宣告一個測試用例失敗,msg是失敗資訊。
failureException(msg)
是unittest.TestCase的屬性,用來表示失敗的異常,預設被賦值為AssertionError。
longMessage
預設被賦值為False,如果賦值為True,可以在結果中包含更詳細的diff資訊。
maxDiff
預設長度80*8,用來控制diff顯示的長度。
測試用例資訊查詢
countTestCases()
返回測試用例的個數,對於TestCase例項來說,這個返回值一直是1.
defaultTestResult()
如果在run()方法中未提供result引數,該函式返回一個包含本用例測試結果的TestResult物件。
id()
返回測試用例的編號,通常是如下格式:模組名.類名.函式名。可以用於測試結果的輸出。
shortDescription()
返回測試用例的描述,即函式的docstring,如果沒有,返回None。可以用於測試結果輸出中描述測試內容。
addCleanup(function, *args, **kwargs)
新增針對每個測試用例執行完tearDown()方法之後的清理方法,新增進去的函式按照後進先出(LIFO)的順序執行,當然,如果setUp()方法執行失敗,那麼不會執行tearDown()方法,自然也不會執行addCleanup()裡新增的函式。
doCleanups()
無條件強制呼叫addCleanup()新增的函式,適用於setUp()方法執行失敗但是需要執行清理函式的場景,或者希望在tearDown()方法之前執行這些清理函式。
TestSuite類
class unittest.TestSuite(tests=())
TestSuite類用於將測試用例分組,比如實際工作中需要將測試用例按照優先順序分類。tests引數是一個可迭代的物件,每個物件可以是測試用例,也可以是測試套,比如:
import unittest
class A(unittest.TestCase):
def test_a(self):
print 'test_a'
class B(unittest.TestCase):
def test_b(self):
print 'test_b'
def test_c(self):
print 'test_c'
if __name__ == '__main__':
suite1 = unittest.TestSuite(tests=[B('test_c'), A('test_a')])
suite = unittest.TestSuite(tests=(suite1, B('test_b')))
unittest.TextTestRunner(verbosity=2).run(suite)
執行結果如下:
TestSuite類中定義瞭如下方法:
addTest(test)
新增測試用例,test引數可以是一個TestCase例項或者TestSuite例項。
addTests(tests)
tests引數是一個由測試用例或測試套組成的可迭代物件。
run(result)
執行測試套中包含的用例,將結果儲存到result引數對應的TestResult物件中,比如:
import unittest
class A(unittest.TestCase):
def test_a(self):
print 'test_a'
class B(unittest.TestCase):
def test_b(self):
print 'test_b'
def test_c(self):
print 'test_c'
if __name__ == '__main__':
suite1 = unittest.TestSuite(tests=[B('test_c'), A('test_a')])
suite = unittest.TestSuite()
suite.addTest(suite1)
r = unittest.TestResult()
suite.run(r)
print r.__dict__
執行結果如下:
debug()
與TestCase中的debug()中的功能相同,執行測試用例,如果有異常,將異常上報給呼叫者。
countTestCases()
返回測試套中測試用例的數量,比如:
import unittest
class A(unittest.TestCase):
def test_a(self):
print 'test_a'
class B(unittest.TestCase):
def test_b(self):
print 'test_b'
def test_c(self):
print 'test_c'
if __name__ == '__main__':
suite1 = unittest.TestSuite(tests=[B('test_c'), A('test_a')])
suite = unittest.TestSuite(tests=(suite1, B('test_b')))
print suite.countTestCases()
執行結果為:3。
如果覺得我的文章對您有幫助,歡迎關注我(CSDN:Mars Loo的部落格)或者為這篇文章點贊,謝謝!