一、一些概念性問題:
白盒測試——把測試對象看作一個打開的盒子,程序內部的邏輯結構和其他信息對測試人員是公開的。
回歸測試——軟件或環境的修復或更正后的“再測試”,自動測試工具對這類測試尤其有用。
單元測試——是最小粒度的測試,以測試某個功能或代碼塊。一般由程序員來做,因為它需要知道內部程序設計和編碼的細節。
二、單元測試的好處 :
A、提高開發速度——測試是以自動化方式執行的,提升了測試代碼的執行效率。
B、提高軟件代碼質量——它使用小版本發布至集成,便于實現人員除錯。同時引入重構概念,讓代碼更干凈和富有彈性。
C、提升系統的可信賴度——它是回歸測試的一種。支持修復或更正后的“再測試”,可確保代碼的正確性。
三、單元測試的針對對象 :
A、面向過程的軟件開發針對過程。
B、面向對象的軟件開發針對對象。
C、可以做類測試,功能測試,接口測試(最常用于測試類中的方法)。
四、下面是JUnit一些特性的總結:
1) 提供的API可以讓你寫出測試結果明確的可重用單元測試用例
2) 提供了三種方式來顯示你的測試結果,而且還可以擴展
3) 提供了單元測試用例成批運行的功能
4) 超輕量級而且使用簡單,沒有商業性的欺騙和無用的向導
5) 整個框架設計良好,易擴展,對不同性質的被測對象,如Class,Jsp,Servlet,Ejb等,Junit有不同的使用技巧。
6) 使用斷言方法判斷期望值和實際值差異,返回Boolean值。
7) 測試驅動設備使用共同的初始化變量或者實例。
8) 測試包結構便于組織和集成運行。
9) 支持圖型交互模式和文本交互模式。
五、JUnit的好處和JUnit單元測試編寫原則:
好處: A、可以使測試代碼與產品代碼分開。
B、針對某一個類的測試代碼通過較少的改動便可以應用于另一個類的測試。
C、易于集成到測試人員的構建過程中,JUnit和Ant的結合可以實施增量開發。
D、JUnit是公開源代碼的,可以進行二次開發。
E、可以方便地對JUnit進行擴展。
編寫原則:
A、是簡化測試的編寫,這種簡化包括測試框架的學習和實際測試單元的編寫。
B、是使測試單元保持持久性。
C、是可以利用既有的測試來編寫相關的測試。
六、JUnit框架組成 :
A、對測試目標進行測試的方法與過程集合,可稱為測試用例(TestCase)。
B、測試用例的集合,可容納多個測試用例(TestCase),將其稱作測試包(TestSuite)。
C、測試結果的描述與記錄。(TestResult) 。
D、測試過程中的事件監聽者(TestListener)。
E、每一個測試方法所發生的與預期不一致狀況的描述,稱其測試失敗元素(TestFailure)
F、JUnit Framework中的出錯異常(AssertionFailedError)。
JUnit框架是一個典型的Composite模式:TestSuite可以容納任何派生自Test的對象;當調用TestSuite對象的run()方法是,會遍歷自己容納的對象,逐個調用它們的run()方法
七、JUnit中常用的接口和類 :
1)Test接口——運行測試和收集測試結果
Test接口使用了Composite設計模式,是單獨測試用例 (TestCase),聚合測試模式(TestSuite)及測試擴展(TestDecorator)的共同接口。
它的public int countTestCases()方法,它來統計這次測試有多少個TestCase,另外一個方法就是public void run( TestResult ),TestResult是實例接受測試結果, run方法執行本次測試。
2)TestCase抽象類——定義測試中固定方法
TestCase是Test接口的抽象實現,(不能被實例化,只能被繼承)其構造函數TestCase(string name)根據輸入的測試名稱name創建一個測試實例。由于每一個TestCase在創建時都要有一個名稱,若某測試失敗了,便可識別出是哪個測試失敗。
TestCase類中包含的setUp()、tearDown()方法。setUp()方法集中初始化測試所需的所有變量和實例,并且在依次調用測試類中的每個測試方法之前再次執行setUp()方法。tearDown()方法則是在每個測試方法之后,釋放測試程序方法中引用的變量和實例。
開發人員編寫測試用例時,只需繼承TestCase,來完成run方法即可,然后JUnit獲得測試用例,執行它的run方法,把測試結果記錄在TestResult之中。
3)Assert靜態類——一系列斷言方法的集合
Assert包含了一組靜態的測試方法,用于期望值和實際值比對是否正確,即測試失敗,Assert類就會拋出一個AssertionFailedError異常,JUnit測試框架將這種錯誤歸入Failes并加以記錄,同時標志為未通過測試。如果該類方法中指定一個String類型的傳參則該參數將被做為AssertionFailedError異常的標識信息,告訴測試人員改異常的詳細信息。
JUnit 提供了6大類31組斷言方法,包括基礎斷言、數字斷言、字符斷言、布爾斷言、對象斷言。
其中assertEquals(Object expcted,Object actual)內部邏輯判斷使用equals()方法,這表明斷言兩個實例的內部哈希值是否相等時,最好使用該方法對相應類實例的值進行比較。而assertSame(Object expected,Object actual)內部邏輯判斷使用了Java運算符“==”,這表明該斷言判斷兩個實例是否來自于同一個引用(Reference),最好使用該方法對不同類的實例的值進行比對。asserEquals(String message,String expected,String actual)該方法對兩個字符串進行邏輯比對,如果不匹配則顯示著兩個字符串有差異的地方。ComparisonFailure類提供兩個字符串的比對,不匹配則給出詳細的差異字符。
4)TestSuite測試包類——多個測試的組合
TestSuite類負責組裝多個Test Cases。待測得類中可能包括了對被測類的多個測試,而TestSuit負責收集這些測試,使我們可以在一個測試中,完成全部的對被測類的多個測試。
TestSuite類實現了Test接口,且可以包含其它的TestSuites。它可以處理加入Test時的所有拋出的異常。
TestSuite處理測試用例有6個規約(否則會被拒絕執行測試)
A 測試用例必須是公有類(Public)
B 測試用例必須繼承與TestCase類
C 測試用例的測試方法必須是公有的( Public )
D 測試用例的測試方法必須被聲明為Void
E 測試用例中測試方法的前置名詞必須是test
F 測試用例中測試方法誤任何傳遞參數
5)TestResult結果類和其它類與接口
TestResult結果類集合了任意測試累加結果,通過TestResult實例傳遞個每個測試的Run()方法。TestResult在執行TestCase是如果失敗會異常拋出
TestListener接口是個事件監聽規約,可供TestRunner類使用。它通知listener的對象相關事件,方法包括測試開始startTest(Test test),測試結束endTest(Test test),錯誤,增加異常addError(Test test,Throwable t)和增加失敗addFailure(Test test,AssertionFailedError t)
TestFailure失敗類是個“失敗”狀況的收集類,解釋每次測試執行過程中出現的異常情況。其toString()方法返回“失敗”狀況的簡要描述。
八、JUnit的擴展應用 :
JUnit + HttpUnit=WEB功能測試工具
JUnit + hansel =代碼覆蓋測試工具
JUnit + abbot =界面自動回放測試工具
JUnit + dbunit =數據庫測試工具
JUnit + junitperf=性能測試工具
九、測試的種類:
單元測試:檢測模塊(也就是類)的正確性。如果對象需要訪問外部的數據
資源,例如數據庫,就需要模擬一個mock objects,但在實際中真實數據與測試環境是不同的。 客戶測試:這是功能性、系統、和驗收測試。用來測試整體的系統特性。在XP中,這些測試由用戶編寫。
綜合測試:介于用戶測試和單元測試之間的橋梁。綜合測試幫助測試應用
程序的交互性。一般情況下,mock objects不被用于綜合測試,它會增加測試時間。同樣,綜合測
試經常依賴特殊的測試環境,例如數據庫送來的測試數據。綜合測試也需要用到外部類庫。例如為J2EE應用
程序進行綜合測試的類庫Cactus。
開發人員測試:這是用來讓開發人員檢驗自己代碼或新函數的。對于每一個開發人員,只要有可能,就需要有更多的測試來檢驗代碼。組織這
些測試和組織程序代碼一樣
重要。
十、測試驅動開發(TDD)優勢:
TDD的基本思路就是通過測試來推動整個開發的進行。而測試驅動開發技術并不只是單純的測試工作。
需求向來就是軟件開發過程中感覺最不好明確描述、易變的東西。這里說的需求不只是指用戶的需求,還包括對代碼的使用需求。很多開發人員最害怕的就是后期還要修改某個類或者函數的接口進行修改或者擴展,為什么會發生這樣的事情就是因為這部分代碼的使用需求沒有很好的描述。測試驅動開發就是通過編寫測試用例,先考慮代碼的使用需求(包括功能、過程、接口等),而且這個描述是無二義的,可執行驗證的。
通過編寫這部分代碼的測試用例,對其功能的分解、使用過程、接口都進行了設計。而且這種從使用角度對代碼的設計通常更符合后期開發的需求。可測試的要求,對代碼的內聚性的提高和復用都非常有益。因此測試驅動開發也是一種代碼設計的過程。
開發人員通常對編寫文檔非常厭煩,但要使用、理解別人的代碼時通常又希望能有文檔進行指導。而測試驅動開發過程中產生的測試用例代碼就是對代碼的最好的解釋。
快樂工作的基礎就是對自己有信心,對自己的工作成果有信心。當前很多開發人員卻經常在擔心:“代碼是否正確?”“辛苦編寫的代碼還有沒有嚴重bug?”“修改的新代碼對其他部分有沒有影響?”。這種擔心甚至導致某些代碼應該修改卻不敢修改的地步。測試驅動開發提供的測試集就可以作為你信心的來源。
當然測試驅動開發最重要的功能還在于保障代碼的正確性,能夠迅速發現、定位bug。而迅速發現、定位bug是很多開發人員的夢想。針對關鍵代碼的測試集,以及不斷完善的測試用例,為迅速發現、定位bug提供了條件。
我的一段功能非常復雜的代碼使用TDD開發完成,真實環境應用中只發現幾個bug,而且很快被定位解決。您在應用后,也一定會為那種自信的開發過程,功能不斷增加、完善的感覺,迅速發現、定位bug的能力所感染,喜歡這個技術的。
十一、測試驅動開發(TDD)原理:
測試驅動開發的基本思想就是在開發功能代碼之前,先編寫測試代碼。也就是說在明確要開發某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關的代碼滿足這些測試用例。然后循環進行添加其他功能,直到完全部功能的開發。
我們這里把這個技術的應用領域從代碼編寫擴展到整個開發過程。應該對整個開發過程的各個階段進行測試驅動,首先思考如何對這個階段進行測試、驗證、考核,并編寫相關的測試文檔,然后開始下一步工作,最后再驗證相關的工作。下圖是一個比較流行的測試模型:V測試模型。
【圖 V測試模型】
在開發的各個階段,包括需求分析、概要設計、詳細設計、編碼過程中都應該考慮相對應的測試工作,完成相關的測試用例的設計、測試方案、測試計劃的編寫。這里提到的開發階段只是舉例,根據實際的開發活動進行調整。相關的測試文檔也不一定是非常詳細復雜的文檔,或者什么形式,但應該養成測試驅動的習慣。
關于測試模型,還有X測試模型。這個測試模型,我認為,是對詳細階段和編碼階段進行建模,應該說更詳細的描述了詳細設計和編碼階段的開發行為。及針對某個功能進行對應的測試驅動開發。
【圖 X測試模型】
十一、測試驅動開發(TDD)過程:
軟件開發其他階段的測試驅動開發,根據測試驅動開發的思想完成對應的測試文檔即可。下面針對詳細設計和編碼階段進行介紹。測試驅動開發的基本過程如下:
1) 明確當前要完成的功能。可以記錄成一個 TODO 列表。
2) 快速完成針對此功能的測試用例編寫。
3) 測試代碼編譯不通過。
4) 編寫對應的功能代碼。
5) 測試通過。
6) 對代碼進行重構,并保證測試通過。
7) 循環完成所有功能的開發。
開發過程中,通常把測試代碼和功能代碼分開存放,這里提供一個簡單的測試框架使用例子,您可以通過它了解測試框架的使用。下面是文件列表。
project/ 項目主目錄
project/test 測試項目主目錄
project/test/testSeq.cpp 測試seq_t 的測試文件,對其他功能文件的測試文件復制后修改即可
project/test/testSeq.h
project/test/Makefile 測試項目的 Makefile
project/test/main.cpp 測試項目的主文件,不需要修改
project/main.cpp 項目的主文件
project/seq_t.h 功能代碼,被測試文件
project/Makefile 項目的 Makefile
十二、測試驅動開發(TDD)原則:
測試隔離。不同代碼的測試應該相互隔離。對一塊代碼的測試只考慮此代碼的測試,不要考慮其實現細節(比如它使用了其他類的邊界條件)。
一頂帽子。開發人員開發過程中要做不同的工作,比如:編寫測試代碼、開發功能代碼、對代碼重構等。做不同的事,承擔不同的角色。開發人員完成對應的工作時應該
保持注意力集中在當前工作上,而不要過多的考慮其他方面的細節,保證頭上只有一頂帽子。避免考慮無關細節過多,無謂地增加復雜度。
測試列表。需要測試的功能點很多。應該在任何階段想添加功能需求問題時,把相關功能點加到測試列表中,然后繼續手頭工作。然后不斷的完成對應的測試用例、功能
代碼、重構。一是避免疏漏,也避免干擾當前進行的工作。
測試驅動。這個比較核心。完成某個功能,某個類,首先編寫測試代碼,考慮其如何使用、如何測試。然后在對其進行設計、編碼。
先寫斷言。測試代碼編寫時,應該首先編寫對功能代碼的判斷用的斷言語句,然后編寫相應的輔助語句。
可測試性。功能代碼設計、開發時應該具有較強的可測試性。其實遵循比較好的設計原則的代碼都具備較好的測試性。比如比較高的內聚性,盡量依賴于接口等。
及時重構。無論是功能代碼還是測試代碼,對結構不合理,重復的代碼等情況,在測試通過后,及時進行重構。
小步前進。軟件開發是個復雜性非常高的工作,開發過程中要考慮很多東西,包括代碼的正確性、可擴展性、性能等等,很多問題都是因為復雜性太大導致的。極限編程
提出了一個非常好的思路就是小步前進。把所有的規模大、復雜性高的工作,分解成小的任務來完成。對于一個類來說,一個功能一個功能的完成,如果太困
難就再分解。每個功能的完成就走測試代碼-功能代碼-測試-重構的循環。通過分解降低整個系統開發的復雜性。這樣的效果非常明顯。幾個小的功能代碼
完成后,大的功能代碼幾乎是不用調試就可以通過。一個個類方法的實現,很快就看到整個類很快就完成啦。本來感覺很多特性需要增加,很快就會看到沒有
幾個啦。你甚至會為這個速度感到震驚。(我理解,是大幅度減少調試、出錯的時間產生的這種速度感)
十三、測試驅動開發(TDD)測試技術:
1. 測試范圍、粒度
對哪些功能進行測試?會不會太繁瑣?什么時候可以停止測試?這些問題比較常見。按大師 Kent Benk 的話,對那些你認為應該測試的代碼進行測試。就是說,要相信自己的感覺,自己的經驗。那些重要的功能、核心的代碼就應該重點測試。感到疲勞就應該停下來休息一下。感覺沒有必要更詳細的測試,就停止本輪測試。
測試驅動開發強調測試并不應該是負擔,而應該是幫助我們減輕工作量的方法。而對于何時停止編寫測試用例,也是應該根據你的經驗,功能復雜、核心功能的代碼就應該編寫更全面、細致的測試用例,否則測試流程即可。
測試范圍沒有靜態的標準,同時也應該可以隨著時間改變。對于開始沒有編寫足夠的測試的功能代碼,隨著bug的出現,根據bug補齊相關的測試用例即可。
小步前進的原則,要求我們對大的功能塊測試時,應該先分拆成更小的功能塊進行測試,比如一個類A使用了類B、C,就應該編寫到A使用B、C功能的測試代碼前,完成對B、C的測試和開發。那么是不是每個小類或者小函數都應該測試哪?我認為沒有必要。你應該運用你的經驗,對那些可能出問題的地方重點測試,感覺不可能出問題的地方就等它真正出問題的時候再補測試吧。
2. 怎么編寫測試用例
測試用例的編寫就用上了傳統的測試技術。
- 操作過程盡量模擬正常使用的過程。
- 全面的測試用例應該盡量做到分支覆蓋,核心代碼盡量做到路徑覆蓋。
- 測試數據盡量包括:真實數據、邊界數據。
- 測試語句和測試數據應該盡量簡單,容易理解。
- 為了避免對其他代碼過多的依賴,可以實現簡單的樁函數或樁類(Mock Object)。
- 如果內部狀態非常復雜或者應該判斷流程而不是狀態,可以通過記錄日志字符串的方式進行驗證。