全面介紹python中很常用的單元測(cè)試框架unitest
1、unitest主要功能模塊介紹
unitest主要包含TestCase、TestSuite、TestLoader、TextTestRunner、TextTestResult這幾個(gè)功能模塊。
- TestCase:一個(gè)TestCase實(shí)例就是一個(gè)測(cè)試用例,一個(gè)測(cè)試用例就是一個(gè)完整的測(cè)試流程,包括測(cè)試前環(huán)境的搭建,測(cè)試代碼的執(zhí)行,以及測(cè)試后環(huán)境的還原或者銷毀。元測(cè)試的本質(zhì)也就在這里,一個(gè)測(cè)試用例是一個(gè)完整的測(cè)試單元,可以對(duì)某一具體問(wèn)題進(jìn)行檢查驗(yàn)證。
- TestSuite:多個(gè)測(cè)試用例集合在一起就是TestSuite,TestSuite也可以嵌套TestSuite。
- TestLoader:TestLoader的作用是將Testcase加載到TestSuite中。
- TextTestRunner:TextTestRunner是用來(lái)執(zhí)行測(cè)試用例的,其中的run(test)會(huì)執(zhí)行TestSuite/TestCase中的run(result)方法。
- TextTestResult:TextTestResult用來(lái)保存測(cè)試結(jié)果,其中包括運(yùn)行了多少測(cè)試用例,成功了多少,失敗了多少等信息。
整個(gè)流程為:寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來(lái)運(yùn)行TestSuite,運(yùn)行的結(jié)果保存在TextTestResult中。
2、實(shí)例介紹
首先準(zhǔn)備幾個(gè)待測(cè)的方法,寫在test_func.py中。
def add(a, b): return a + b def multi(a, b): return a * b def lower_str(string): return string.lower() def square(x): return x ** 2
準(zhǔn)備好幾個(gè)待測(cè)的方法之后,為這些方法寫一個(gè)測(cè)試用例,寫入our_testcase.py中。
import unittest
from test_func import *
class TestFunc(unittest.TestCase):
"""Test test_func.py"""
def test_add(self):
"""Test func add"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(1, 3))
def test_multi(self):
"""Test func multi"""
self.assertEqual(6, multi(2, 3))
self.assertNotEqual(8, multi(3, 3))
def test_lower_str(self):
"""Test func lower_str"""
self.assertEqual("abc", lower_str("ABC"))
self.assertNotEqual("Dce", lower_str("DCE"))
def test_square(self):
"""Test func square"""
self.assertEqual(17, square(4)) # 這里故意設(shè)計(jì)一個(gè)會(huì)出錯(cuò)的用例,測(cè)試4的平方等于17,實(shí)際上并不等于。
self.assertNotEqual(35, square(6))
if __name__ == '__main__':
unittest.main()
這里寫好之后,進(jìn)入命令行終端,執(zhí)行python our_testcase.py,執(zhí)行結(jié)果如下。
...F ====================================================================== FAIL: test_square (__main__.TestFunc) Test func square ---------------------------------------------------------------------- Traceback (most recent call last): File "our_testcase.py", line 27, in test_square self.assertEqual(17, square(4)) AssertionError: 17 != 16 ---------------------------------------------------------------------- Ran 4 tests in 0.000s FAILED (failures=1)
這里分析一下這個(gè)執(zhí)行結(jié)果。首先能夠看到一共運(yùn)行了4個(gè)測(cè)試用例,失敗了1個(gè),并且給出了失敗原因,AssertionError: 17 != 16,這是我們故意留下的錯(cuò)誤漏洞,被測(cè)試用例測(cè)試出來(lái)了。
第一行...F中,一個(gè)點(diǎn).代表測(cè)試成功,F(xiàn)代表失敗,我們的測(cè)試結(jié)果中,前三個(gè)成功了,第四個(gè)失敗了,總共是四個(gè)測(cè)試,其余的符號(hào)中E代表出錯(cuò),S代表跳過(guò)。
特別說(shuō)明的一點(diǎn)是,測(cè)試的執(zhí)行順序跟方法的順序沒有關(guān)系,四個(gè)測(cè)試是隨機(jī)先后執(zhí)行的。
每個(gè)測(cè)試方法編寫的時(shí)候,都要以test開頭,比如test_square,否則是不被unitest識(shí)別的。
在unitest.main()中加上verbosity參數(shù)可以控制輸出的錯(cuò)誤報(bào)告的詳細(xì)程序,默認(rèn)是1,如果設(shè)為0,則不輸出每一用例的執(zhí)行結(jié)果,即上面的第一行的執(zhí)行結(jié)果內(nèi)容。如果設(shè)為2,則輸出詳細(xì)的執(zhí)行結(jié)果。
修改our_testcase.py中主函數(shù)。
if __name__ == '__main__': unittest.main(verbosity=2)
執(zhí)行結(jié)果如下。
test_add (__main__.TestFunc) Test func add ... ok test_lower_str (__main__.TestFunc) Test func lower_str ... ok test_multi (__main__.TestFunc) Test func multi ... ok test_square (__main__.TestFunc) Test func square ... FAIL ====================================================================== FAIL: test_square (__main__.TestFunc) Test func square ---------------------------------------------------------------------- Traceback (most recent call last): File "our_testcase.py", line 27, in test_square self.assertEqual(17, square(4)) AssertionError: 17 != 16 ---------------------------------------------------------------------- Ran 4 tests in 0.000s FAILED (failures=1)
可以看到,每一個(gè)用例的詳細(xì)執(zhí)行情況以及用例名,用例描述均被輸出了出來(lái),在測(cè)試方法下加代碼示例中的"""Doc String""",在用例執(zhí)行時(shí),會(huì)將該字符串作為此用例的描述,加合適的注釋能夠使輸出的測(cè)試報(bào)告更加便于閱讀。
3、組織TestSuite
按照上面的測(cè)試方法,我們無(wú)法控制用例執(zhí)行的順序,這樣顯然是不合理的,因?yàn)樵谝恍y(cè)試過(guò)程中,我們肯定需要控制先測(cè)試某些用例,再測(cè)試某些用例,這些用例有先后的因果關(guān)系。在這里,我們就需要用到TestSuite。我們添加到TestSuite中的case是會(huì)按照添加的順序執(zhí)行的。
還有一個(gè)問(wèn)題是,我們現(xiàn)在只有一個(gè)測(cè)試文件,我們直接執(zhí)行該文件即可,但如果有多個(gè)測(cè)試文件,怎么進(jìn)行組織,總不能一個(gè)個(gè)文件執(zhí)行吧,答案也在TestSuite中。
新建一個(gè)文件,test_suite.py。
import unittest
from our_testcase import TestFunc
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [TestFunc("test_square"), TestFunc("test_lower_str"), TestFunc("test_multi")]
suite.addTests(tests)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
執(zhí)行結(jié)果如下。
test_square (our_testcase.TestFunc) Test func square ... FAIL test_lower_str (our_testcase.TestFunc) Test func lower_str ... ok test_multi (our_testcase.TestFunc) Test func multi ... ok ====================================================================== FAIL: test_square (our_testcase.TestFunc) Test func square ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/luyuze/projects/test/our_testcase.py", line 27, in test_square self.assertEqual(17, square(4)) AssertionError: 17 != 16 ---------------------------------------------------------------------- Ran 3 tests in 0.000s FAILED (failures=1)
這樣,用例執(zhí)行的順序就是按照我們添加進(jìn)去的順序來(lái)執(zhí)行的了。
上面使用的是TestSuite的addTests()方法,并直接傳入TestCase列表,也有一些其他的方法可以向TestSuite中添加用例。
# 直接用addTest方法添加單個(gè)TestCase
suite.addTest(TestMathFunc("test_multi"))
# 使用loadTestFromName,傳入模塊名.TestCase名,下面?zhèn)z方法效果相同
suite.addTests(unittest.TestLoader().loadTestsFromName('our_testcase.TestFunc'))
suite.addTests(unittest.TestLoader().loadTestsFromNames(['our_testcase.TestFunc']))
# loadTestsFromTestCase(),傳入TestCase
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFunc))
用TestLoader的方法是無(wú)法對(duì)case進(jìn)行排序的,同時(shí),suite中也可以套suite。
4、輸出文件
用例組織好了,但是結(jié)果只能輸出到控制臺(tái),這樣沒辦法查看之前的執(zhí)行記錄,我們想將結(jié)果輸出到文件。
修改test_suite.py。
import unittest
from our_testcase import TestFunc
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [TestFunc("test_square"), TestFunc("test_lower_str"), TestFunc("test_multi")]
suite.addTests(tests)
with open('UnitestTextReport.txt', 'a') as f:
runner = unittest.TextTestRunner(stream=f, verbosity=2)
runner.run(suite)
5、測(cè)試前后的處理
在之前的測(cè)試中,可能會(huì)存在這樣的問(wèn)題:如果要在測(cè)試之前準(zhǔn)備環(huán)境,測(cè)試完成之后做一些清理怎么辦?這里需要用到的是setUp()和tearDown()。
修改our_testcase.py。
import unittest
from test_func import *
class TestFunc(unittest.TestCase):
"""Test test_func.py"""
def setUp(self):
print("do something before testcase")
def test_add(self):
"""Test func add"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(1, 3))
def test_multi(self):
"""Test func multi"""
self.assertEqual(6, multi(2, 3))
self.assertNotEqual(8, multi(3, 3))
def test_lower_str(self):
"""Test func lower_str"""
self.assertEqual("abc", lower_str("ABC"))
self.assertNotEqual("Dce", lower_str("DCE"))
def test_square(self):
"""Test func square"""
self.assertEqual(17, square(4))
self.assertNotEqual(35, square(6))
def tearDownClass(self):
print("do something after testcase")
if __name__ == '__main__':
unittest.main(verbosity=2)
執(zhí)行結(jié)果:
test_add (__main__.TestFunc) Test func add ... do something before testcase do something after testcase ok test_lower_str (__main__.TestFunc) Test func lower_str ... do something before testcase do something after testcase ok test_multi (__main__.TestFunc) Test func multi ... do something before testcase do something after testcase ok test_square (__main__.TestFunc) Test func square ... do something before testcase do something after testcase FAIL ====================================================================== FAIL: test_square (__main__.TestFunc) Test func square ---------------------------------------------------------------------- Traceback (most recent call last): File "our_testcase.py", line 30, in test_square self.assertEqual(17, square(4)) AssertionError: 17 != 16 ---------------------------------------------------------------------- Ran 4 tests in 0.001s FAILED (failures=1)
可以發(fā)現(xiàn)setUp()和tearDown()在每個(gè)case前后都執(zhí)行了一次。如果要在所有case執(zhí)行之前和所有case執(zhí)行之后準(zhǔn)備和清理環(huán)境,我們可以使用setUpClass() 與 tearDownClass()。
class TestFunc(unittest.TestCase):
"""Test test_func.py"""
@classmethod
def setUpClass(cls):
print "This setUpClass() method only called once."
@classmethod
def tearDownClass(cls):
print "This tearDownClass() method only called once too."
6、跳過(guò)case
如果我們臨時(shí)想要跳過(guò)某個(gè)case不執(zhí)行,unitest也有相應(yīng)的方法。
1、skip裝飾器
# -*- coding: utf-8 -*-
import unittest
from test_func import *
class TestFunc(unittest.TestCase):
"""Test test_func.py"""
@unittest.skip('do not run this case')
def test_add(self):
"""Test func add"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(1, 3))
def test_multi(self):
"""Test func multi"""
self.assertEqual(6, multi(2, 3))
self.assertNotEqual(8, multi(3, 3))
def test_lower_str(self):
"""Test func lower_str"""
self.assertEqual("abc", lower_str("ABC"))
self.assertNotEqual("Dce", lower_str("DCE"))
def test_square(self):
"""Test func square"""
self.assertEqual(17, square(4))
self.assertNotEqual(35, square(6))
if __name__ == '__main__':
unittest.main(verbosity=2)
執(zhí)行結(jié)果:
test_add (__main__.TestFunc) Test func add ... skipped 'do not run this case' test_lower_str (__main__.TestFunc) Test func lower_str ... ok test_multi (__main__.TestFunc) Test func multi ... ok test_square (__main__.TestFunc) Test func square ... FAIL ====================================================================== FAIL: test_square (__main__.TestFunc) Test func square ---------------------------------------------------------------------- Traceback (most recent call last): File "our_testcase.py", line 28, in test_square self.assertEqual(17, square(4)) AssertionError: 17 != 16 ---------------------------------------------------------------------- Ran 4 tests in 0.000s FAILED (failures=1, skipped=1)
結(jié)果顯示為,總共執(zhí)行4個(gè)測(cè)試,1個(gè)失敗,1個(gè)被跳過(guò)。
skip裝飾器一共有三個(gè) unittest.skip(reason)、unittest.skipIf(condition, reason)、unittest.skipUnless(condition, reason),skip無(wú)條件跳過(guò),skipIf當(dāng)condition為True時(shí)跳過(guò),skipUnless當(dāng)condition為False時(shí)跳過(guò)。
2、TestCase.skipTest()方法
class TestFunc(unittest.TestCase):
"""Test test_func.py"""
def test_add(self):
"""Test func add"""
self.skipTest("do not run this case")
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(1, 3))
效果與第一種是一樣的。
以上就是全面介紹python中很常用的單元測(cè)試框架unitest的詳細(xì)內(nèi)容,更多關(guān)于python 單元測(cè)試框架unitest的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python實(shí)現(xiàn)批量圖片格式轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)批量圖片格式轉(zhuǎn)換的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
在django中使用post方法時(shí),需要增加csrftoken的例子
這篇文章主要介紹了在django中使用post方法時(shí),需要增加csrftoken的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
在Anaconda3下使用清華鏡像源安裝TensorFlow(CPU版)
這篇文章主要介紹了在Anaconda3下使用清華鏡像源安裝TensorFlow(CPU版),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Django動(dòng)態(tài)隨機(jī)生成溫度前端實(shí)時(shí)動(dòng)態(tài)展示源碼示例
本篇文章主要描述的是在動(dòng)態(tài)隨機(jī)生成溫度,在前端動(dòng)態(tài)實(shí)時(shí)展示,主要用到兩個(gè)東西,一個(gè)是APScheduler定時(shí)任務(wù) 和websocket,最后利用echarts將數(shù)據(jù)展示出來(lái),下面對(duì)這兩個(gè)分別進(jìn)行詳細(xì)的解說(shuō)2021-09-09
python實(shí)現(xiàn)批量轉(zhuǎn)換文件編碼(批轉(zhuǎn)換編碼示例)
這篇文章主要介紹了python實(shí)現(xiàn)批量轉(zhuǎn)換文件編碼示例,指定文件編碼、目錄或擴(kuò)展名即可進(jìn)行轉(zhuǎn)換,大家參考使用吧2014-01-01
使用PyQt5實(shí)現(xiàn)一個(gè)鼠標(biāo)連點(diǎn)器
這篇文章主要為大家詳細(xì)介紹了如何使用PyQt5實(shí)現(xiàn)一個(gè)鼠標(biāo)連點(diǎn)器,從而對(duì)QVBoxLayout、QHBoxLayout和QStackedWidget進(jìn)行一個(gè)回顧復(fù)習(xí),需要的可以參考一下2023-12-12
淺談numpy數(shù)組中冒號(hào)和負(fù)號(hào)的含義
下面小編就為大家分享一篇淺談numpy數(shù)組中冒號(hào)和負(fù)號(hào)的含義,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04

