一、什么是單元測試
單元測試是用來對一個模塊、一個函數或者一個類來進行正確性檢驗的測試工作。
比如對于函數abs(),我們可以編寫的測試用例為:
(1)輸入正數,比如1、1.2、0.99,期待返回值與輸入相同
(2)輸入復數,比如-1、-1.2、-0.99,期待返回值與輸入相反
(3)輸入0,期待返回0
(4)輸入非數值類型,比如None、[]、{}、期待拋出TypeError
把上面這些測試用例放到一個測試模塊里,就是一個完整的單元測試
?
二、unittest工作原理
unittest中最核心的四部分是:TestCase,TestSuite,TestRunner,TestFixture
(1)一個TestCase的實例就是一個測試用例。測試用例就是指一個完整的測試流程,包括測試前準備環境的搭建(setUp),執行測試代碼(run),以及測試后環境的還原(tearDown)。元測試(unit test)的本質也就在這里,一個測試用例是一個完整的測試單元,通過運行這個測試單元,可以對某一個問題進行驗證。
(2)而多個測試用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。
(3)TestLoader是用來加載TestCase到TestSuite中的。
(4)TextTestRunner是來執行測試用例的,其中的run(test)會執行TestSuite/TestCase中的run(result)方法
(5)測試的結果會保存到TextTestResult實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息。
?
綜上,整個流程就是首先要寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中,整個過程集成在unittest.main模塊中。
?
三、下面舉兩個實例,來看看unittest如何測試一個簡單的函數
(1)編寫一個Dict類,這個類的行為和dict一致,但是可以通過屬性來訪問例如
[python] view plaincopy
>>>?d?=?Dict(a=1,?b=2)??>>>?d['a']??1??>>>?d.a??1?? ?
mydict.py代碼如下:
[python] view plaincopy
class?Dict(dict):??????def?__init__(self,?**kw):??????????super(Dict,?self).__init__(**kw)?????????def?__getattr__(self,?key):??????????try:??????????????return?self[key]??????????except?KeyError:??????????????raise?AttributeError(r"'Dict'?object?has?no?attribute?'%s'"?%?key)?????????def?__setattr__(self,?key,?value):??????????self[key]?=?value?????
用于測試的文件mydict_test.py代碼如下:
[python] view plaincopy
import?unittest??from?mydict?import?Dict????????class?TestDict(unittest.TestCase):??????def?test_init(self):??????????d?=?Dict(a=1,?b='test')??????????self.assertEqual(d.a,?1)??????????self.assertEqual(d.b,?'test')??????????self.assertTrue(isinstance(d,?dict))?????????def?test_key(self):??????????d?=?Dict()??????????d['key']?=?'value'??????????self.assertEqual(d.key,?'value')?????????def?test_attr(self):??????????d?=?Dict()??????????d.key?=?'value'??????????self.assertTrue('key'?in?d)??????????self.assertEqual(d['key'],?'value')?????????def?test_keyerror(self):??????????d?=?Dict()??????????with?self.assertRaises(KeyError):??????????????value?=?d['empty']?????????def?test_attrerror(self):??????????d?=?Dict()??????????with?self.assertRaises(AttributeError):??????????????value?=?d.empty????????if?__name__?==?'__main__':??????unittest.main()??
直接把mydict_test.py當普通的Python腳本運行即可
輸出:
[python] view plaincopy
.....??----------------------------------------------------------------------??Ran?5?tests?in?0.000s?????OK??
(2)測一個簡單的加減乘除接口
mathfunc.py文件代碼如下:
[python] view plaincopy
def?add(a,?b):??????return?a?+?b?????def?minus(a,?b):??????return?a?-?b?????def?multi(a,?b):??????return?a?*?b?????def?divide(a,?b):??????return?a?/?b??
test_mathfunc.py文件代碼如下:
[python] view plaincopy
import?unittest??from?mathfunc?import?*????????class?TestMathFunc(unittest.TestCase):?????????def?test_add(self):??????????self.assertEqual(3,?add(1,?2))??????????self.assertNotEqual(3,?add(2,?2))?????????def?test_minus(self):??????????self.assertEqual(1,?minus(3,?2))?????????def?test_multi(self):??????????self.assertEqual(6,?multi(3,?2))?????????def?test_divide(self):??????????self.assertEqual(2,?divide(6,?3))??????????self.assertEqual(2.5,?divide(5,?2))?????if?__name__?==?'__main__':??<span?style="white-space:pre;">?</span>unittest.main()??
輸出:
[python] view plaincopy
.F..??======================================================================??FAIL:?test_divide?(__main__.TestDict)??----------------------------------------------------------------------??Traceback?(most?recent?call?last):????File?"D:/pythonWorkspace/test_mathfunc.py",?line?20,?in?test_divide??????self.assertEqual(2.5,?divide(5,?2))??AssertionError:?2.5?!=?2?????----------------------------------------------------------------------??Ran?4?tests?in?0.000s?????FAILED?(failures=1)??
可以看到一共運行了4個測試,失敗了1個,并且給出了失敗原因,2.5!=2,也就是說我們的divide方法是有問題的。
?
關于輸出的幾點說明:
1、在第一行給出了每一個用例執行的結果的標識,成功是.,失敗是F,出錯是E,跳過是S。從上面可以看出,測試的執行跟方法的順序沒有關系,divide方法寫在了第4個,但是卻在第2個執行。
2、每個測試方法均以test開頭,否則不能被unittest識別
3、在uniitest.main()中加verbosity參數可以控制輸出的錯誤報告的詳細程度,默認是1,如果設為0, 則不輸出每一用例的執行結果,即沒有上面的結果中的第1行,如果設為2,則輸出詳細的執行結果,如下所示:
?
[python] view plaincopy
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",?line?20,?in?test_divide??????self.assertEqual(2.5,?divide(5,?2))??AssertionError:?2.5?!=?2?????----------------------------------------------------------------------??Ran?4?tests?in?0.000s?????FAILED?(failures=1)??
四、組織TestSuite
上面的測試用例在執行的時候沒有按照順序執行,如果想要讓用例按照你設置的順序執行就用到了TestSuite。我們添加到TestSuite中的case是會按照添加的順序執行的。
現在我們只有一個測試文件,如果有多個測試文件,也可以用TestSuite組織起來。
繼續上面第二加減乘除的例子,現在再新建一個文件,test_suite.py,代碼如下:
[python] view plaincopy
import?unittest??from?test_mathfunc?import?TestMathFunc?????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)??
執行結果如下:
[python] view plaincopy
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",?line?20,?in?test_divide??????self.assertEqual(2.5,?divide(5,?2))??AssertionError:?2.5?!=?2?????----------------------------------------------------------------------??Ran?3?tests?in?0.000s?????FAILED?(failures=1)??
五、將結果輸出到文件
現在我們的測試結果只能輸出到控制臺,現在我們想將結果輸出到文件中以便后續可以查看。
將test_suite.py進行一點修改,代碼如下:
[python] view plaincopy
???import?unittest??from?test_mathfunc?import?TestMathFunc?????if?__name__?==?'__main__':??????suite?=?unittest.TestSuite()?????????tests?=?[TestMathFunc("test_add"),?TestMathFunc("test_minus"),?TestMathFunc("test_divide")]??????suite.addTests(tests)?????????with?open('UnittestTextReport.txt',?'a')?as??f:??????????runner?=?unittest.TextTestRunner(stream=f,?verbosity=2)??????????runner.run(suite)?????
運行該文件,就會發現目錄下生成了'UnittestTextReport.txt,所有的執行報告均輸出到了此文件中。
?
六、test fixture的setUp和tearDown
當遇到要啟動一個數據庫這種情況時,只想在開始時連接上數據庫,在結束時關閉連接。那么可以使用setUp和tearDown函數。
[python] view plaincopy
class?TestDict(unittest.TestCase):?????????def?setUp(self):??????????print?'setUp...'?????????def?tearDown(self):??????????print?'tearDown...'??
這兩個方法在每個測試方法執行前以及執行后執行一次,setUp用來為測試準備環境,tearDown用來清理環境,以備后續的測試。
?
如果想要在所有case執行之前準備一次環境,并在所有case執行結束之后再清理環境,我們可以用setUpClass()與tearDownClass(),代碼格式如下:
[python] view plaincopy
class?TestMathFunc(unittest.TestCase):??????@classmethod??????def?setUpClass(cls):??????????print?"setUp"????????@classmethod??????def?tearDownClass(cls):??????????print?"tearDown"??
七、跳過某個case
unittest提供了幾種方法可以跳過case
(1)skip裝飾器
?
代碼如下
[python] view plaincopy
import?unittest??from?mathfunc?import?*?????class?TestMathFunc(unittest.TestCase):??????????.....????????@unittest.skip("i?don't?want?to?run?this?case.")??????def?test_minus(self):??????????self.assertEqual(1,?minus(3,?2))??
輸出:
[python] view plaincopy
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",?line?28,?in?test_divide??????self.assertEqual(2.5,?divide(5,?2))??AssertionError:?2.5?!=?2?????----------------------------------------------------------------------??Ran?3?tests?in?0.000s?????FAILED?(failures=1,?skipped=1)??
skip裝飾器一共有三個
unittest,skip(reason):無條件跳過
unittest.skipIf(condition, reason):當condition為True時跳過
unittest.skipUnless(condition, reason):當condition為False時跳過
?
(2)TestCase.skipTest()方法
?
[python] view plaincopy
class?TestMathFunc(unittest.TestCase):??...??def?test_minus(self):??????????self.skipTest('do?not?run?this.')??????????self.assertEqual(1,?minus(3,?2))?? ?
輸出:
[python] view plaincopy
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",?line?20,?in?test_divide??????self.assertEqual(2.5,?divide(5,?2))??AssertionError:?2.5?!=?2?????----------------------------------------------------------------------??Ran?3?tests?in?0.000s?????FAILED?(failures=1,?skipped=1)??
八、用HTMLTestRunner輸出漂亮的HTML報告
txt格式的文本執行報告過于簡陋,這里我們學習一下借助HTMLTestRunner生成HTML報告。首先需要下載HTMLTestRunner.py,并放到當前目錄下,或者python目錄下的Lib中,就可以導入運行了。
下載地址:http://tungwaiyip.info/software/HTMLTestRunner.html
?
將test_suite.py代碼修改如下:
[python] view plaincopy
???import?unittest??from?test_mathfunc?import?TestMathFunc??from?HTMLTestRunner?import?HTMLTestRunner????????if?__name__?==?'__main__':??????suite?=?unittest.TestSuite()?????????tests?=?[TestMathFunc("test_add"),?TestMathFunc("test_minus"),?TestMathFunc("test_divide")]??????suite.addTests(tests)?????????with?open('HTMLReport.html',?'w')?as?f:??????????runner?=?HTMLTestRunner(stream=f,??????????????????????????????????title?=?'MathFunc?Test?Report',??????????????????????????????????description='generated?by?HTMLTestRunner.',??????????????????????????????????verbosity=2??????????????????????????????????)??????????runner.run(suite)??
執行后,控制臺輸出如下:
[python] view plaincopy
ok?test_add?(test_mathfunc.TestMathFunc)??F??test_divide?(test_mathfunc.TestMathFunc)?????Time?Elapsed:?0:00:00.001000??
生成的html:
?
?
九、總結
1、unittest是python自帶的單元測試框架,我們可以用其來作為我們自動化測試框架的用例組織執行框架。
2、unittest的流程:寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中,我們通過命令行或者unittest.main()執行時,main會調用TextTestRunner中的run來執行,或者我們可以直接通過TextTestRunner來執行用例。
3、一個class繼承unittest.TestCase即是一個TestCase,其中以 test 開頭的方法在load時被加載為一個真正的TestCase。
4、verbosity參數可以控制執行結果的輸出,0 是簡單報告、1 是一般報告、2 是詳細報告。
5、可以通過addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。
6、用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例執行前布置環境,以及在用例執行后清理環境
7、我們可以通過skip,skipIf,skipUnless裝飾器跳過某個case,或者用TestCase.skipTest方法。
8、參數中加stream,可以將報告輸出到文件:可以用TextTestRunner輸出txt報告,以及可以用HTMLTestRunner輸出html報告。
轉載于:https://www.cnblogs.com/qidongbo/p/8558720.html
總結
以上是生活随笔為你收集整理的python单元测试之unittest框架使用的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。