朱道雨
摘 要 本文首先介紹了測試驅(qū)動開發(fā),繼而介紹了測試模式,描述了測試驅(qū)動開發(fā)的優(yōu)勢、一般步驟、測試的原則和對一般開發(fā)人員的要求,最后比較詳細地介紹了幾種基于Python的測試框架。
關(guān)鍵詞 測試驅(qū)動開發(fā) Python 測試框架
中圖分類號:TP3 文獻標(biāo)識碼:A
1測試驅(qū)動開發(fā)介紹
測試驅(qū)動開發(fā)(TDD)是一種不同于傳統(tǒng)開發(fā)流程的新型的開發(fā)方法,采取了很小的增量式開發(fā)方式,強調(diào)測試在軟件開發(fā)過程中的作用。TDD是一種編碼之前進行單元測試的軟件開發(fā)思想。TDD先行定義了需求,擴大測試覆蓋率,從而推進整個開發(fā)過程。其編寫的代碼簡潔、健壯且高質(zhì)量,加快軟件開發(fā)。
2測試模式介紹
針對軟件開發(fā)典型的瀑布模型,其是一種帶有部分反饋的自上而下的開發(fā)模式,對文檔支持要求高。該模型按照階段劃分了檢查點,最終產(chǎn)品只能在后期才能看到最終結(jié)果,測試階段一般占一半時間。軟件測試和軟件開發(fā)過程一樣遵循軟件工程原理,下面以V模型和X模式為例對測試與開發(fā)之間的關(guān)系進行說明。
2.1 V測試模型
V模型和瀑布開發(fā)模型一樣,存在需求分析、概要設(shè)計、詳細設(shè)計、代碼編碼等階段,這些過程都需考慮對應(yīng)的測試工作,同時設(shè)計測試用例、編制測試計劃。V模型明確標(biāo)明了測試的不同級別,清楚地描述了測試階段和開發(fā)過程期間各階段的對應(yīng)的關(guān)系。需求分析產(chǎn)生的錯誤直到驗收測試才能發(fā)現(xiàn)是其最大的局限性。
2.2 X測試模型
圖1:X模型圖
如圖,X模型更詳細地描述了詳細設(shè)計和編碼階段的開發(fā)行為,分離單程序片段的編碼和測試,直到集成為可執(zhí)行程序,然后測試這些可執(zhí)行程序。已通過集成測試的成品既可封裝并提交給用戶,也可作為更大規(guī)模集成的一部分,針對多程序片段,分離編碼和測試,并定義了探索性測試,強調(diào)單元測試和集成測試的重要性。
3 TDD的優(yōu)勢以及一般步驟
3.1 TDD優(yōu)勢
TDD相較于傳統(tǒng)的結(jié)構(gòu)化開發(fā)過程方法,具有以下優(yōu)勢:
3.1.1更快更好地迎接需求變化
根據(jù)客戶需求編寫測試用例,關(guān)注用戶反饋和以使用者的角度對功能的過程和接口進行設(shè)計,可及時響應(yīng)需求變更,更符合后期開發(fā)的需求。同時覆蓋完全的單元測試,提高了代碼的可靠性。
3.1.2明顯地縮短了設(shè)計決策的反饋循環(huán)
基于易測試和測試獨立性的要求,更多地依賴于接口,通過實現(xiàn)松耦合的設(shè)計,提高系統(tǒng)的可擴展性和抗變性;同時對其功能的分解、使用過程、接口都進行了設(shè)計。
3.1.3降低了后續(xù)測試的成本
TDD先考慮代碼的使用需求――功能、過程、接口等,再確認需求是無歧義的、可驗證的。首先將測試提到編碼之前并頻繁地運行所有測試,盡早地發(fā)現(xiàn)或避免錯誤,不斷地重構(gòu)代碼并消除重復(fù)設(shè)計,提高代碼的重用,極大地提高代碼的健壯性。
3.1.4提供持續(xù)的回歸測試
TDD能幫助開發(fā)人員持續(xù)地跟蹤軟件生命周期內(nèi)的全部狀態(tài),并快速地反饋開發(fā)人員,使開發(fā)人員無需過分擔(dān)心不可預(yù)知的異常并大膽地實施重構(gòu)。
3.1.5提供合格的文檔
TDD所產(chǎn)生的單元測試代碼是最完美的開發(fā)者文檔,含有API如何使用的信息,同時與最新的代碼保持同步。
3.2 TDD的一般步驟
(1)明確當(dāng)前主要完成的功能,并形成電子或者書面的記錄表單;
(2)快速地編寫此功能的測試用例;
(3)運行測試,發(fā)現(xiàn)新增的測試不能通過測試;
(4)改動測試程序,盡快讓測試程序通過運行
(5)重構(gòu)已完成的代碼,消除重復(fù)設(shè)計,優(yōu)化程序結(jié)構(gòu),并保證測試通過
(6)循環(huán)完成所有功能。
3.3測試原則
(1)測試隔離,不同代碼的測試相互隔離,對單塊代碼的測試應(yīng)只考慮該代碼的測試,
(2)測試列表,在軟件開發(fā)的任意階段,添加新功能需求時,需將相關(guān)功能點添加到測試列表中,不斷地完成測試用例,并重構(gòu)。
(3)測試驅(qū)動,在完成代碼類、功能設(shè)計前,首先編制測試代碼,考慮如何使用與測試,繼而設(shè)計編碼。
(4)測試與斷言優(yōu)先,在編寫代碼之前編寫測試,在編制代碼過程中,首先編寫斷言語句,再編寫輔助語句。
(5)可測試性,代碼設(shè)計過程必須具有較強的可測試性,有助于提高代碼的內(nèi)聚性和復(fù)用。
(6)及時重構(gòu),無論是功能代碼還是測試代碼,對結(jié)構(gòu)不合理、重復(fù)的代碼,都要進行及時的重構(gòu)。
4測試框架
4.1 unittest測試框架
該測試框架中最核心的概念為:test case,test suite,test runner,text fixture。unittest提供了TestCase基類來構(gòu)造單元測試用例,在構(gòu)建測試用例的時候,使用setUp方法初始化測試環(huán)境,tearDown方法執(zhí)行掃尾結(jié)束工作并還原測試環(huán)境,這類似于上下文管理器,setUp和tearDown每次必須運行一次(如圖2)。
圖2:測試流程圖
TestLoader用來加載TestCase、TestSuite,可使用LoadTestsForm方法,創(chuàng)建測試實例,繼而add到TestSuite中,返回一個TestSuite實例。使用TextTestRunner執(zhí)行測試用例,其中,測試結(jié)果保存到Result中,包括運行的用例數(shù)量、成功與失敗信息。Fixture完成測試環(huán)境的搭建和銷毀。
通過命令行或者main函數(shù)來啟動單元測試,main會調(diào)用run來執(zhí)行,當(dāng)Runner執(zhí)行時,將執(zhí)行結(jié)果輸出到控制臺或文件,通過設(shè)置verbosity參數(shù)控制執(zhí)行結(jié)果的輸出。unittest框架比較笨重且難以擴展,必須在TestCase子類中編寫所有的單元測試和斷言方法,必須為測試活動建立測試套件。
4.2 doctest測試
doctest測試框架在文檔字符串(docstring)內(nèi)嵌注釋,這些注釋將各單元模塊的期望結(jié)果予以表達,字符串如同交互式shell會話搜索Python代碼,繼而嘗試執(zhí)行并驗證結(jié)果。測試用例的位置必須放在整個模塊文件的開始部分或緊接聲明語句的下一行。設(shè)置verbose 參數(shù)用于控制是否輸出詳細信息,默認不輸出任何東西,除非測試失敗。
當(dāng)不帶任何參數(shù)運行 test_basic.py 時。將test_basic 導(dǎo)入到腳本的命名空間中,可導(dǎo)入其他希望要測試的模塊。testmod函數(shù)去遍歷模塊本身、模塊函數(shù)及其所有文檔字符串,以找出所有類似交互式 shell 會話的內(nèi)容。
doctest 在解析顯式會話時,將空行作為會話結(jié)束來處理??衫梦臋n字符串自定義的函數(shù)來解決此問題。一方面由于文檔字符串畢竟是字符串換碼,所以要注意\n這樣的序列被擴展。另一方面由 doctest處理的文檔字符串可能會在其內(nèi)部包含回溯。
4.3 nose 系列
由于擴展自unittest的擴展,nose 也支持類似于 setUp、setUpClass、setUpModule 的測試環(huán)境創(chuàng)建方式,所以python的測試更加簡單。nose自動發(fā)現(xiàn)測試代碼并執(zhí)行,nose提供了大量的插件,比如測試輸出的xUnitcompatible,覆蓋報表等等。
路徑、模塊(文件)、類、函數(shù)的名字若匹配上testMatch,那就會被認為是一個用例,另外所有 unittest.TestCase 的子類都會被當(dāng)做測試用例。nose主要檢查一個測試用例的文件夾,否則會忽略此文件路徑。顯式地避免某個對象被當(dāng)做測試用例的方法是:給其或其容器添加一個 _test_ 屬性,并且運算結(jié)果不為 True,只要 bool(_test_) == False 即可。
nose可不使用特定的格式、不需要一個類容器,甚至不需要 import,但nose測試用例的命名仍需以 test_開頭。
4.4 pytest
作為unittest的替代工具,pytest是一個語法簡單且功能豐富靈活的測試框架。相比unittest,實現(xiàn)相同的測試功能,pytest更簡潔高效;支持簡單的單元測試和復(fù)雜的功能測試,支持參數(shù)化,支持重復(fù)執(zhí)行失敗的case,支持運行由nose、unittest編寫的測試case。
pytest 創(chuàng)建測試環(huán)境(fixture)的方式,通過顯式指定scope參數(shù)來選擇裝飾器。一個fixture 函數(shù)的類型一旦定義即確定,對于 scope=function 的 fixture 函數(shù)會在測試用例的前后分別調(diào)用 setup/teardown。測試用例的參數(shù)只負責(zé)引用具體的對象,并不關(guān)心作用域;同時setup/teardown 語法與 unittest 的兼容性不如 nose 高。fixture在參數(shù)列表中可以明了地知道哪些tests使用了一個資源,無需人為地創(chuàng)建類,只需分離fixture應(yīng)用。而對于一個資源而言,teardown代碼是緊密地和setup代碼耦合的。常常需在資源setup代碼的位置處指定資源的生命周期的范圍來節(jié)省測試時間。
一般而言,測試類以Test開頭的類,但不能帶有 _init_方法;測試文件以test開頭或者結(jié)尾的文件;測試函數(shù)以test開頭。pytest會自動進行收集測試用例,在pytest.ini配置文件中,python_classes指定測試類和測試文件、python_functions 指定測試函數(shù)。在setup.cfg文件中norecursedirs 指定禁止目錄。
4.5 unittest2測試框架
unittest2測試框架是unittest的升級版,改善了API以及診斷語法。由于篇幅所限,只說明一點:可使用import unittest2 as unittest在unittest與unittest2之間進行切換。
5結(jié)束語
構(gòu)建“TDD友好”的系統(tǒng),需要進行分層架構(gòu),建立標(biāo)準(zhǔn)一致的系統(tǒng),操作環(huán)境可控,努力創(chuàng)建高內(nèi)聚低耦合的結(jié)構(gòu),在TDD實踐過程可實現(xiàn)代碼的低層設(shè)計。而TDD 并不能直接提高設(shè)計能力,它要求開發(fā)人員思想上重視測試,掌握重構(gòu)與設(shè)計模式等知識,從而間接改善軟件設(shè)計。
參考文獻
[1] Beck,K.測試驅(qū)動開發(fā)(中文版) [M].孫平平譯.北京:中國電力出版社, 2004.
[2] 雨痕.Python3學(xué)習(xí)筆記(上卷)[M].北京:電子工業(yè)出版社,2018.
[3] Matthew,K.Python機器學(xué)習(xí)實踐,測試驅(qū)動的開發(fā)方法[M].劉江一,上官明喬,白皓,劉旭威譯.北京:機械工業(yè)出版社,2017.