(轉(zhuǎn)自測(cè)試前輩)
單元測(cè)試的重要性就不多說(shuō)了,可惡的是python中有太多的單元測(cè)試框架和工具涌哲,什么unittest, testtools, subunit,
coverage, testrepository, nose, mox, mock, fixtures,
discover羹与,再加上setuptools,
distutils等等這些沮脖,先不說(shuō)如何寫(xiě)單元測(cè)試熏瞄,光是怎么運(yùn)行單元測(cè)試就有N多種方法葱绒,再因?yàn)樗菧y(cè)試而非功能,是很多人沒(méi)興趣觸及的東西声登。但是作為
一個(gè)優(yōu)秀的程序員狠鸳,不僅要寫(xiě)好功能代碼揣苏,寫(xiě)好測(cè)試代碼一樣的彰顯你的實(shí)力。如此多的框架和工具件舵,很容易讓人困惑卸察,困惑的原因是因?yàn)椴](méi)有理解它的基本原
理,如果一些基本的概念都不清楚铅祸,怎么能夠?qū)懗鏊悸非逦臏y(cè)試代碼坑质?
今天的主題就是unittest,作為標(biāo)準(zhǔn)python中的一個(gè)模塊临梗,是其它框架和工具的基礎(chǔ)涡扼,參考資料是它的官方文檔:http://docs.python.org/2.7/library/unittest.html和源代碼,文檔已經(jīng)寫(xiě)的非常好了盟庞,我在這里記錄的主要是它的一些重要概念吃沪、關(guān)鍵點(diǎn)以及可能會(huì)碰到的一些坑,目的在于對(duì)unittest加深理解什猖,而不是停留在泛泛的表面層上票彪。
unittest是一個(gè)python版本的junit,junit是java中的單元測(cè)試框架不狮,對(duì)java的單元測(cè)試降铸,有一句話(huà)很貼切:Keep the
bar
green,相信使用eclipse寫(xiě)過(guò)java單元測(cè)試的都心領(lǐng)神會(huì)摇零。unittest實(shí)現(xiàn)了很多junit中的概念推掸,比如我們非常熟悉的test
case, test suite等,總之驻仅,原理都是相通的谅畅,只是用不同的語(yǔ)言表達(dá)出來(lái)。
在文檔的開(kāi)篇就介紹了unittest中的4個(gè)重要的概念:test fixture, test case, test suite, test runner雾家,我覺(jué)得只有理解了這幾個(gè)概念铃彰,才能真正的理解單元測(cè)試的基本原理,下面就主要圍繞這幾個(gè)概念來(lái)展開(kāi)這篇文章芯咧。
首先通過(guò)查看unittest的源碼牙捉,來(lái)看一下這幾個(gè)概念,以及他們之間的關(guān)系敬飒,他們是如何在一起工作的邪铲,其靜態(tài)類(lèi)圖如下:
一個(gè)TestCase的實(shí)例就是一個(gè)測(cè)試用例。什么是測(cè)
試用例呢无拗?就是一個(gè)完整的測(cè)試流程带到,包括測(cè)試前準(zhǔn)備環(huán)境的搭建(setUp),執(zhí)行測(cè)試代碼(run)英染,以及測(cè)試后環(huán)境的還原(tearDown)揽惹。元測(cè)
試(unit test)的本質(zhì)也就在這里被饿,一個(gè)測(cè)試用例是一個(gè)完整的測(cè)試單元,通過(guò)運(yùn)行這個(gè)測(cè)試單元搪搏,可以對(duì)某一個(gè)問(wèn)題進(jìn)行驗(yàn)證狭握。
而多個(gè)測(cè)試用例集合在一起,就是TestSuite疯溺,而且TestSuite也可以嵌套TestSuite论颅。
TestLoader是用來(lái)加載TestCase到TestSuite中的,其中有幾個(gè)loadTestsFrom__()方法囱嫩,就是從各個(gè)地方尋找TestCase恃疯,創(chuàng)建它們的實(shí)例,然后add到TestSuite中墨闲,再返回一個(gè)TestSuite實(shí)例今妄。
TextTestRunner是來(lái)執(zhí)行測(cè)試用例的,其中的run(test)會(huì)執(zhí)行TestSuite/TestCase中的run(result)方法鸳碧。
測(cè)試的結(jié)果會(huì)保存到TextTestResult實(shí)例中蛙奖,包括運(yùn)行了多少測(cè)試用例,成功了多少杆兵,失敗了多少等信息。
這樣整個(gè)流程就清楚了仔夺,首先是要寫(xiě)好TestCase琐脏,然后由TestLoader加載TestCase到TestSuite,然后由
TextTestRunner來(lái)運(yùn)行TestSuite缸兔,運(yùn)行的結(jié)果保存在TextTestResult中日裙,整個(gè)過(guò)程集成在unittest.main模
塊中。
現(xiàn)在已經(jīng)涉及到了test case, test suite, test runner這三個(gè)概念了惰蜜,還有test fixture沒(méi)有提到昂拂,那什么是test fixture呢?抛猖?在TestCase的docstring中有這樣一段話(huà):
Test authors
should subclass TestCase for their own tests. Construction?and
deconstruction of the test's environment ('fixture') can be?implemented
by overriding the 'setUp' and 'tearDown' methods respectively.
可見(jiàn)格侯,對(duì)一個(gè)測(cè)試用例環(huán)境的搭建和銷(xiāo)毀,是一個(gè)fixture财著,通過(guò)覆蓋
TestCase的setUp()和tearDown()方法來(lái)實(shí)現(xiàn)联四。這個(gè)有什么用呢?比如說(shuō)在這個(gè)測(cè)試用例中需要訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)撑教,那么可以在setUp()
中建立數(shù)據(jù)庫(kù)連接以及進(jìn)行一些初始化朝墩,在tearDown()中清除在數(shù)據(jù)庫(kù)中產(chǎn)生的數(shù)據(jù),然后關(guān)閉連接伟姐。注意tearDown的過(guò)程很重要收苏,要為以后的
TestCase留下一個(gè)干凈的環(huán)境亿卤。關(guān)于fixture,還有一個(gè)專(zhuān)門(mén)的庫(kù)函數(shù)叫做fixtures鹿霸,功能更加強(qiáng)大排吴,以后會(huì)介紹到。
至此杜跷,概念和流程基本清楚了傍念,下面通過(guò)簡(jiǎn)單的例子再來(lái)實(shí)踐一下,就拿unittest文檔上的例子吧:
TestSequenceFunctions繼承自u(píng)nittest.TestCase葛闷,重寫(xiě)了setUp()方法憋槐,并且定義了三個(gè)以'test'開(kāi)頭的方法,那這個(gè)TestSequenceFunctions類(lèi)到底是個(gè)什么呢淑趾?它是一個(gè)測(cè)試用例阳仔,還是三個(gè)測(cè)試用例?說(shuō)是三個(gè)測(cè)試用例的話(huà)扣泊,它本身繼承自TestCase近范,說(shuō)是一個(gè)測(cè)試用例的話(huà),里面又有三個(gè)test_*()方法延蟹,明顯是三個(gè)測(cè)試用例评矩。其實(shí),我們只要看一些TestLoader是如何加載測(cè)試用例的阱飘,就一清二楚了斥杜,在loader.TestLoader類(lèi)中有一個(gè)loadTestsFromTestCase()方法:
getTestCaseNames()是從TestCase這個(gè)類(lèi)中找所有以“test”開(kāi)頭的方法,然后注意第9行沥匈,在構(gòu)造TestSuite對(duì)象時(shí)蔗喂,其參數(shù)使用了一個(gè)map方法,即對(duì)testCaseNames中的每一個(gè)元素高帖,使用testCaseClass為其構(gòu)造對(duì)象缰儿,其結(jié)果是一個(gè)TestCase的對(duì)象集合,可以用下面的代碼來(lái)分步說(shuō)明:
可見(jiàn)散址,對(duì)每一個(gè)以test開(kāi)頭的方法乖阵,都為其構(gòu)建了一個(gè)TestCase對(duì)象,值得注意的是预麸,如果沒(méi)有定義test開(kāi)頭的方法义起,而是將測(cè)試代碼寫(xiě)到了一個(gè)名為runTest的方法中,那么會(huì)為該runTest方法構(gòu)建TestCase對(duì)象师崎,如果定義了test開(kāi)頭的方法默终,就會(huì)忽略runTest方法。至此,基本就清楚了齐蔽,每一個(gè)以test開(kāi)頭的方法两疚,都會(huì)為其構(gòu)建TestCase對(duì)象,也就是說(shuō)TestSequenceFunctions類(lèi)中其實(shí)定義了三個(gè)TestCase含滴,之所以寫(xiě)成這樣诱渤,是為了方便,因?yàn)檫@幾個(gè)測(cè)試用例的fixture是相同的谈况,如果每一個(gè)測(cè)試用例單獨(dú)寫(xiě)成一個(gè)TestCase的話(huà)勺美,會(huì)有很多的冗余代碼