python_unittest框架總結(jié)

版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請標明原文地址,謝謝 ^_^ https://blog.csdn.net/xiaoquantouer/article/details/75089200

unittest 官方文檔:https://docs.python.org/3/library/unittest.html

unittest 中文文檔:https://blog.csdn.net/ljl6158999/article/details/80994979

一、什么是單元測試

單元測試是用來對一個模塊、一個函數(shù)或者一個類來進行正確性檢驗的測試工作。

比如對于函數(shù)abs(),我們可以編寫的測試用例為:

(1)輸入正數(shù),比如1、1.2、0.99,期待返回值與輸入相同

(2)輸入復(fù)數(shù),比如-1、-1.2、-0.99,期待返回值與輸入相反

(3)輸入0,期待返回0

(4)輸入非數(shù)值類型,比如None、[]、{}、期待拋出TypeError

把上面這些測試用例放到一個測試模塊里,就是一個完整的單元測試


二、unittest工作原理

unittest中最核心的四部分是:TestCase,TestSuite,TestRunner,TestFixture

(1)一個TestCase的實例就是一個測試用例。測試用例就是指一個完整的測試流程,包括測試前準備環(huán)境的搭建(setUp),執(zhí)行測試代碼(run),以及測試后環(huán)境的還原(tearDown)。元測試(unit test)的本質(zhì)也就在這里,一個測試用例是一個完整的測試單元,通過運行這個測試單元,可以對某一個問題進行驗證。

(2)而多個測試用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。

(3)TestLoader是用來加載TestCase到TestSuite中的。

(4)TextTestRunner是來執(zhí)行測試用例的,其中的run(test)會執(zhí)行TestSuite/TestCase中的run(result)方法

(5)測試的結(jié)果會保存到TextTestResult實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息。


綜上,整個流程就是首先要寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來運行TestSuite,運行的結(jié)果保存在TextTestResult中,整個過程集成在unittest.main模塊中。


三、下面舉兩個實例,來看看unittest如何測試一個簡單的函數(shù)

(1)編寫一個Dict類,這個類的行為和dict一致,但是可以通過屬性來訪問例如

>>> d = Dict(a=1, b=2)

>>> d['a']

1

>>> d.a

1

mydict.py代碼如下:

classDict(dict):

def__init__(self, **kw):

? ? ? ? super(Dict, self).__init__(**kw)

def__getattr__(self, key):

try:

returnself[key]

exceptKeyError:

raiseAttributeError(r"'Dict' object has no attribute '%s'"% key)

def__setattr__(self, key, value):

? ? ? ? self[key] = value

用于測試的文件mydict_test.py代碼如下:

importunittest

frommydictimportDict

classTestDict(unittest.TestCase):

deftest_init(self):

d = Dict(a=1, b='test')

self.assertEqual(d.a,1)# 判斷d.a是否等于1

self.assertEqual(d.b,'test')# 判斷d.b是否等于test

self.assertTrue(isinstance(d, dict))# 判斷d是否是dict類型

deftest_key(self):

? ? ? ? d = Dict()

d['key'] ='value'

self.assertEqual(d.key,'value')

deftest_attr(self):

? ? ? ? d = Dict()

d.key ='value'

self.assertTrue('key'ind)

self.assertEqual(d['key'],'value')

deftest_keyerror(self):

? ? ? ? d = Dict()

withself.assertRaises(KeyError):# 通過d['empty']訪問不存在的key時,斷言會拋出keyerror

value = d['empty']

deftest_attrerror(self):

? ? ? ? d = Dict()

withself.assertRaises(AttributeError):# 通過d.empty訪問不存在的key時,我們期待拋出AttributeError

? ? ? ? ? ? value = d.empty

if__name__ =='__main__':

? ? unittest.main()

直接把mydict_test.py當(dāng)普通的Python腳本運行即可

輸出:

.....

----------------------------------------------------------------------

Ran5testsin0.000s

OK

(2)測一個簡單的加減乘除接口

mathfunc.py文件代碼如下:

defadd(a, b):

returna + b

defminus(a, b):

returna - b

defmulti(a, b):

returna * b

defdivide(a, b):

returna / b

test_mathfunc.py文件代碼如下:

importunittest

frommathfuncimport*

classTestMathFunc(unittest.TestCase):

deftest_add(self):

self.assertEqual(3, add(1,2))

self.assertNotEqual(3, add(2,2))

deftest_minus(self):

self.assertEqual(1, minus(3,2))

deftest_multi(self):

self.assertEqual(6, multi(3,2))

deftest_divide(self):

self.assertEqual(2, divide(6,3))

self.assertEqual(2.5, divide(5,2))

if__name__ =='__main__':

unittest.main()

輸出:

.F..

======================================================================

FAIL: test_divide (__main__.TestDict)

----------------------------------------------------------------------

Traceback (most recent call last):

File"D:/pythonWorkspace/test_mathfunc.py", line20,intest_divide

self.assertEqual(2.5, divide(5,2))

AssertionError:2.5!=2

----------------------------------------------------------------------

Ran4testsin0.000s

FAILED (failures=1)

可以看到一共運行了4個測試,失敗了1個,并且給出了失敗原因,2.5!=2,也就是說我們的divide方法是有問題的。


關(guān)于輸出的幾點說明:

1、在第一行給出了每一個用例執(zhí)行的結(jié)果的標識,成功是.,失敗是F,出錯是E,跳過是S。從上面可以看出,測試的執(zhí)行跟方法的順序沒有關(guān)系,divide方法寫在了第4個,但是卻在第2個執(zhí)行。

2、每個測試方法均以test開頭,否則不能被unittest識別

3、在uniitest.main()中加verbosity參數(shù)可以控制輸出的錯誤報告的詳細程度,默認是1,如果設(shè)為0, 則不輸出每一用例的執(zhí)行結(jié)果,即沒有上面的結(jié)果中的第1行,如果設(shè)為2,則輸出詳細的執(zhí)行結(jié)果,如下所示:


test_add (__main__.TestMathFunc) ... ok

test_divide (__main__.TestMathFunc) ... FAIL

test_minus (__main__.TestMathFunc) ... ok

test_multi (__main__.TestMathFunc) ... ok

======================================================================

FAIL: test_divide (__main__.TestMathFunc)

----------------------------------------------------------------------

Traceback (most recent call last):

File"D:/pythonWorkspace/test_mathfunc.py", line20,intest_divide

self.assertEqual(2.5, divide(5,2))

AssertionError:2.5!=2

----------------------------------------------------------------------

Ran4testsin0.000s

FAILED (failures=1)

四、組織TestSuite

上面的測試用例在執(zhí)行的時候沒有按照順序執(zhí)行,如果想要讓用例按照你設(shè)置的順序執(zhí)行就用到了TestSuite。我們添加到TestSuite中的case是會按照添加的順序執(zhí)行的。

現(xiàn)在我們只有一個測試文件,如果有多個測試文件,也可以用TestSuite組織起來。

繼續(xù)上面第二加減乘除的例子,現(xiàn)在再新建一個文件,test_suite.py,代碼如下:

# coding=utf-8

importunittest

fromtest_mathfuncimportTestMathFunc

if__name__ =='__main__':

? ? suite = unittest.TestSuite()

tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]

? ? suite.addTests(tests)

runner = unittest.TextTestRunner(verbosity=2)

? ? runner.run(suite)

執(zhí)行結(jié)果如下:

test_add (test_mathfunc.TestMathFunc) ... ok

test_minus (test_mathfunc.TestMathFunc) ... ok

test_divide (test_mathfunc.TestMathFunc) ... FAIL

======================================================================

FAIL: test_divide (test_mathfunc.TestMathFunc)

----------------------------------------------------------------------

Traceback (most recent call last):

File"D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line20,intest_divide

self.assertEqual(2.5, divide(5,2))

AssertionError:2.5!=2

----------------------------------------------------------------------

Ran3testsin0.000s

FAILED (failures=1)

五、將結(jié)果輸出到文件

現(xiàn)在我們的測試結(jié)果只能輸出到控制臺,現(xiàn)在我們想將結(jié)果輸出到文件中以便后續(xù)可以查看。

將test_suite.py進行一點修改,代碼如下:

# coding=utf-8

importunittest

fromtest_mathfuncimportTestMathFunc

if__name__ =='__main__':

? ? suite = unittest.TestSuite()

tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]

? ? suite.addTests(tests)

withopen('UnittestTextReport.txt','a')asf:

runner = unittest.TextTestRunner(stream=f, verbosity=2)

? ? ? ? runner.run(suite)

運行該文件,就會發(fā)現(xiàn)目錄下生成了'UnittestTextReport.txt,所有的執(zhí)行報告均輸出到了此文件中。


六、test fixture的setUp和tearDown

當(dāng)遇到要啟動一個數(shù)據(jù)庫這種情況時,只想在開始時連接上數(shù)據(jù)庫,在結(jié)束時關(guān)閉連接。那么可以使用setUp和tearDown函數(shù)。

classTestDict(unittest.TestCase):

defsetUp(self):

print'setUp...'

deftearDown(self):

print'tearDown...'

這兩個方法在每個測試方法執(zhí)行前以及執(zhí)行后執(zhí)行一次,setUp用來為測試準備環(huán)境,tearDown用來清理環(huán)境,以備后續(xù)的測試。


如果想要在所有case執(zhí)行之前準備一次環(huán)境,并在所有case執(zhí)行結(jié)束之后再清理環(huán)境,我們可以用setUpClass()與tearDownClass(),代碼格式如下:

classTestMathFunc(unittest.TestCase):

? ? @classmethod

defsetUpClass(cls):

print"setUp"

? ? @classmethod

deftearDownClass(cls):

print"tearDown"

七、跳過某個case

unittest提供了幾種方法可以跳過case

(1)skip裝飾器


代碼如下

# coding=utf-8

importunittest

frommathfuncimport*

classTestMathFunc(unittest.TestCase):

? ? .....

? ? @unittest.skip("i don't want to run this case.")

deftest_minus(self):

self.assertEqual(1, minus(3,2))

輸出:

test_add (test_mathfunc.TestMathFunc) ... ok

test_minus (test_mathfunc.TestMathFunc) ... skipped"i don't want to run this case."

test_divide (test_mathfunc.TestMathFunc) ... FAIL

======================================================================

FAIL: test_divide (test_mathfunc.TestMathFunc)

----------------------------------------------------------------------

Traceback (most recent call last):

File"D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line28,intest_divide

self.assertEqual(2.5, divide(5,2))

AssertionError:2.5!=2

----------------------------------------------------------------------

Ran3testsin0.000s

FAILED (failures=1, skipped=1)

skip裝飾器一共有三個

unittest,skip(reason):無條件跳過

unittest.skipIf(condition, reason):當(dāng)condition為True時跳過

unittest.skipUnless(condition, reason):當(dāng)condition為False時跳過

(2)TestCase.skipTest()方法


classTestMathFunc(unittest.TestCase):

...

deftest_minus(self):

self.skipTest('do not run this.')

self.assertEqual(1, minus(3,2))

輸出:

test_add (test_mathfunc.TestMathFunc) ... ok

test_minus (test_mathfunc.TestMathFunc) ... skipped'do not run this.'

test_divide (test_mathfunc.TestMathFunc) ... FAIL

======================================================================

FAIL: test_divide (test_mathfunc.TestMathFunc)

----------------------------------------------------------------------

Traceback (most recent call last):

File"D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line20,intest_divide

self.assertEqual(2.5, divide(5,2))

AssertionError:2.5!=2

----------------------------------------------------------------------

Ran3testsin0.000s

FAILED (failures=1, skipped=1)

八、用HTMLTestRunner輸出漂亮的HTML報告

txt格式的文本執(zhí)行報告過于簡陋,這里我們學(xué)習(xí)一下借助HTMLTestRunner生成HTML報告。首先需要下載HTMLTestRunner.py,并放到當(dāng)前目錄下,或者python目錄下的Lib中,就可以導(dǎo)入運行了。

下載地址:http://tungwaiyip.info/software/HTMLTestRunner.html


將test_suite.py代碼修改如下:

# coding=utf-8

importunittest

fromtest_mathfuncimportTestMathFunc

fromHTMLTestRunnerimportHTMLTestRunner

if__name__ =='__main__':

? ? suite = unittest.TestSuite()

tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]

? ? suite.addTests(tests)

withopen('HTMLReport.html','w')asf:

? ? ? ? runner = HTMLTestRunner(stream=f,

title ='MathFunc Test Report',

description='generated by HTMLTestRunner.',

verbosity=2

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? )

? ? ? ? runner.run(suite)

執(zhí)行后,控制臺輸出如下:

ok test_add (test_mathfunc.TestMathFunc)

F? test_divide (test_mathfunc.TestMathFunc)

Time Elapsed:0:00:00.001000

生成的html:


九、總結(jié)

1、unittest是python自帶的單元測試框架,我們可以用其來作為我們自動化測試框架的用例組織執(zhí)行框架。

2、unittest的流程:寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來運行TestSuite,運行的結(jié)果保存在TextTestResult中,我們通過命令行或者unittest.main()執(zhí)行時,main會調(diào)用TextTestRunner中的run來執(zhí)行,或者我們可以直接通過TextTestRunner來執(zhí)行用例。

3、一個class繼承unittest.TestCase即是一個TestCase,其中以 test 開頭的方法在load時被加載為一個真正的TestCase。

4、verbosity參數(shù)可以控制執(zhí)行結(jié)果的輸出,0 是簡單報告、1 是一般報告、2 是詳細報告。

5、可以通過addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。

6、用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例執(zhí)行前布置環(huán)境,以及在用例執(zhí)行后清理環(huán)境

7、我們可以通過skip,skipIf,skipUnless裝飾器跳過某個case,或者用TestCase.skipTest方法。

8、參數(shù)中加stream,可以將報告輸出到文件:可以用TextTestRunner輸出txt報告,以及可以用HTMLTestRunner輸出html報告。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容