天天看點

單元測試總結

 a、單元測試原理歸納

優良的單元測試具有以下的特點:簡稱為 a-trip。

自動性(automatic)

完備性(thorough)

可重複性(repeatable)

獨立性(independent)

專業性(professional)

下面讓我們逐一了解它們的含義。

自動性

單元測試是自動執行的,這裡的自動指兩個方面:1執行測試,2檢查測試結果 

執行測試應該是足夠簡單的,這樣,我們就可以随時随地的進行測試。是以,執行相應測試就應該像在ide中點選一個按鈕,或者在指令行中打一個指令那麼簡單。一些ide甚至會在背景連續的進行測試。 

維護這個環境很關鍵,不要引入哪些會破壞自動測試模型,需要手工進行幹預的單元測試。如果測試需要一些環境(網絡,資料庫,等等),那就把它做為測試的一部分。mock對象可以有效的隔離對外部的依賴。 

運作測試的不光你一個人,還應該有一台機器對所有送出的代碼持續的運作所有的測試。這種自動、無人職守的檢查的作用就像一個定位杆,這種安全機制保證所有送出的東西不會破壞任何測試。理想情況下并不必須這樣子,因為你可以依靠每一個開發人員都會自行運作所有必需的測試。回到現實,可能某個家夥再某個遙遠的地方并沒有執行必需的測試。也許在他的機器上有一些代碼能夠保證一切沒問題,可他們卻沒有送出代碼。這樣雖然在本地可以執行,其他地方就會出問題。 

最後,自動性的含義還有測試必須能夠自行判斷成功還是失敗。讓一個人(你或者其他倒黴蛋)通過檢查測試産生的資料結果來判斷代碼的正确性,這是讓項目失敗不錯的方式。測試的自檢查是一緻回歸的重要特性。人類并不擅長這種重複性的工作,另外,在項目裡,我們還有很多更重要的事情去做。 

測試的自動執行和自動檢查是非常關鍵的,這意味着你不用花太多的心思再這上面,它已經成為了項目的一部分。測試是項目安全保護網的重要組成部分。它在你掉下來時接住你,且不會擋道,這樣你就可以集中精力走"鋼絲"了。

徹底性

良好的測試應該是徹底的,所有可能會出錯的地方都應該測到。如何能夠做到這一點呢?一種極端情況,測試每一行代碼、每一個分支、每一個抛出的異常,諸如此類;另一個極端,隻測試最可能的情況:邊界條件、資料丢失、資料格式無效,等等。這就需要根據項目情況進行區分了。 

如果追求較高的測試覆寫度,那就需要尋求代碼覆寫工具來幫忙了(例如:免費的nounit,quilt,商用的clover),用這些工具可以知道到底有多少代碼是被測試覆寫的。 

有一個事實需要注意,bug在代碼中的分布情況是不均勻的,而是喜歡聚集在有問題的地方(很多昆蟲都這樣,例如:蒼蠅)。 

這種現象引出了一段非常著名的呼聲“别打更新檔,重寫”。通常,從頭重寫一段有一堆問題的代碼的代價和痛苦程度要低得多。當然,有了單元測試,從頭重寫代碼也安全多了,單元測試可以保證新代碼能夠按照預定的執行。

可重複性

測試用例之間是獨立的同時,也應該是獨立于環境的。目标就是保持每個測試能夠以任意順序、不斷的重複執行,且得到同樣的結果。這意味着測試不能依賴于不可控的任何外部環境。 

使用mock對象來隔離測試,保證測試不依賴于外部環境。如果必須依賴一些條件(例如:資料庫),那就要保證這個條件不受其他開發人員的幹擾。每個開發人員都應該有自己的sandbox,不同的資料庫執行個體啦,web伺服器啦。 

沒有可重複性保證,可能會在最糟糕的時侯遇到一些讓你奇怪的問題,更糟糕的是,這些奇怪的問題通常都是虛幻的,并不是一個真正的bug,隻是測試自身的問題。真不值得為這些虛幻的問題浪費時間。 

每個測試用例,每次執行都應該得到同樣的結果。如果測試結果是失敗,那就說明是在代碼裡面存在bug,而不是有其他問題引起的錯誤。

獨立性

測試代碼必須保持精簡、整潔,這就要求測試是專注的、與環境和其他測試隔離的(要記住,别的開發人員可能同時在運作這些測試)。 

在書寫測試時,保證每個測試隻做一件事情。這不是說在一個測試方法裡隻寫一個assert,而是說在一個測試方法裡應該隻專注于一個、或者幾個共同提供某些功能的方法産品代碼。有些時候一個測試方法隻能夠測試了一個複雜的産品代碼方法的一部分,就需要一套測試方法來完整的測試産品代碼的方法了。 

理想情況下,我們希望在測試代碼和潛在的bug之間建立起關聯。也就是說,當一個測試方法fail的時候,能夠定位到對應代碼中的bug。獨立性也意味着test之間沒有互相依賴。每一個test都是可以獨立運作的,而不需要必須在其他test之後才能運作。每一個test應該是一個孤島。

專業性

為單元測試所寫的代碼是貨真價實的,甚至有些人會争辯說,比送出個客戶的源代碼還要貨真價實。這意味着,測試代碼的書寫和維護應該保持和生産代碼一樣的專業水準。産品代碼中必須遵循的常用的設計準則,如:資料封裝、dry原則、高内聚、低耦合等等,在測試代碼中也一樣要遵守。 

測試代碼很容易就會寫成linear的樣式,代碼裡面充次着同樣的内容,不斷的重複同樣的代碼,卻鮮見方法和對象。這樣很糟糕,測試代碼必須和生産代碼按同等對待來書寫。這就要把哪些常用的、重複部分的代碼抽取出來成為一個方法,這樣就可以在多處調用。 

這樣就會慢慢積累出一些測試的方法,可以封裝在一個類裡面了。别争論什麼,就算這個類隻是用來測試的,就建立一個好了。這樣做不進沒問題,而且是應該得到鼓勵的:測試代碼是貨真價實的代碼。某些情況下,我們也許需要建立一個更大的架構,或者建立一個資料驅動的測試工具。 

不要浪費時間測試那些不必要的方面,我們不是為了測試而建立test。測試的完整性要求測試方法的每一個可能産生bug的方面。如果沒有産生bug的可能,就不要做測試。比如get set方法,就沒必要做測試了。但如果這些get set方法中包含了業務邏輯,哪就有必要進行測試了。 

最後,測試代碼應該和生産代碼是同一規模量級的。是的,你絕對沒有看錯。如果産品代碼有20000行,那麼測試代碼至少也應該是20000行,甚至更多。測試代碼的量也很大,就必須保持整潔、精簡,有良好的設計,結構良好的,同生産代碼一樣的專業。 

[摘自]http://www.iteye.com/topic/30932

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

b、單元測試目的

(1)單元測試目的:

首先保證代碼品質。

其次保證代碼的可維護。

再此保證代碼的可擴充。

(2)單元測試的優點

1、它是一種驗證行為。

    程式中的每一項功能都是測試來驗證它的正确性。它為以後的開發提供支緩。就算是開發後期,我們也可以輕松的增加功能或更改程式結構,而不用擔心這個過程中會破壞重要的東西。而且它為代碼的重構提供了保障。這樣,我們就可以更自由的對程式進行改進。

2、它是一種設計行為。

    編寫單元測試将使我們從調用者觀察、思考。特别是先寫測試(test-first),迫使我們把程式設計成易于調用和可測試的,即迫使我們解除軟體中的耦合。

3、它是一種編寫文檔的行為。

    單元測試是一種無價的文檔,它是展示函數或類如何使用的最佳文檔。這份文檔是可編譯、可運作的,并且它保持最新,永遠與代碼同步。

4、它具有回歸性。

    自動化的單元測試避免了代碼出現回歸,編寫完成之後,可以随時随地的快速運作測試。

(3)單元測試的範疇

    如果要給單元測試定義一個明确的範疇,指出哪些功能是屬于單元測試,這似乎很難。但下面讨論的四個問題,基本上可以說明單元測試的範疇,單元測試所要做的工作。

1、 它的行為和我期望的一緻嗎?

    這是單元測試最根本的目的,我們就是用單元測試的代碼來證明它所做的就是我們所期望的。

2、 它的行為一直和我期望的一緻嗎?

    編寫單元測試,如果隻測試代碼的一條正确路徑,讓它正确走一遍,并不算是真正的完成。軟體開發是一個項複雜的工程,在測試某段代碼的行為是否和你的期望一 緻時,你需要确認:在任何情況下,這段代碼是否都和你的期望一緻;譬如參數很可疑、硬碟沒有剩餘空間、緩沖區溢出、網絡掉線的時候。

3、 我可以依賴單元測試嗎?

    不能依賴的代碼是沒有多大用處的。既然單元測試是用來保證代碼的正确性,那麼單元測試也一定要值得依賴。

4、 單元測試說明我的意圖了嗎?

    單元測試能夠幫我們充分了解代碼的用法,從效果上而言,單元測試就像是能執行的文檔,說明了在你用各種條件調用代碼時,你所能期望這段代碼完成的功能。

[摘自]http://www.iteye.com/topic/38205

我想說的就是,在整個軟體行業來說,不管是程式員也好,老闆也好,客戶也好,都漠視了一個基本的事實:單元測試代碼是軟體産品的一個必須組成部分!不提供測試代碼的軟體産品就是偷工減料,以次充好的奸商行為!

[摘自]http://www.iteye.com/topic/14021

c、基于mock對象和junit架構簡化spring web元件單元測試

對于java元件開發者來說,他們都盼望擁有一組能夠對元件開發提供全面測試功能的好用的單元測試。一直以來,與測試獨立的java對象相比,測試傳統型j2ee web元件是一項更為困難的任務,因為web元件必須運作在某種伺服器平台上并且它們還要與基于http的web互動細節相聯系。

易測性(在架構中測試每個元件而不管其具體種類)是spring架構所提倡的關鍵原則之一。從這一角度看,spring是對核心j2ee模型的一個重大改進—在以前情況下,在容器外進行元件測試是很難實作的,而且即使是容器内測試也往往要求複雜的安裝過程。

本文正是想集中探讨spring的易測性特征—它能使得對web元件進行單元測試就象測試普通java對象(pojo)一樣容易。

一、spring mock類簡介

mock對象是一個術語,原來主要流行于extreme程式員和junit小組中。在單元測試上下文中,一個mock對象是指這樣的一個對象——它能夠用一些“虛構的占位符”功能來“模拟”實作一些對象接口。在測試過程中,這些虛構的占位符對象可用簡單方式來模仿對于一個元件的期望的行為和結果,進而讓你專注于元件本身的徹底測試而不用擔心其它依賴性問題。

spring從j2ee的web端為每個關鍵接口提供了一個mock實作:

mockhttpservletrequest—幾乎每個單元測試中都要使用這個類,它是j2ee web應用程式最常用的接口httpservletrequest的mock實作。

mockhttpservletresponse—此對象用于httpservletresponse接口的mock實作。

mockhttpsession—這是另外一個經常使用的mock對象(後文将讨論此類在會話綁定進行中的應用)。

delegatingservletinputstream—這個對象用于servletinputstream接口的mock實作。

delegatingservletoutputstream—這個對象将代理servletoutputstream實作。在需要攔截和分析寫向一個輸出流的内容時,你可以使用它。

總之,在實作你自己的測試控制器時,上面這些對象是最為有用的。然而,spring也提供了下列相應于其它不太常用的元件的mock實作(如果你是一個底層api開發者,那麼你可能會找到其各自的相應用法):

mockexpressionevaluator—這個mock對象主要應用于你想開發并測試你自己的基于jstl的标簽庫時。

mockfilterconfig—這是filterconfig接口的一個mock實作。

mockpagecontext—這是jsp pagecontext接口的一個mock實作。你會發現這個對象的使用有利于測試預編譯的jsp。

mockrequestdispatcher—requestdispatcher接口的一個mock實作,你主要在其它mock對象内使用它。

mockservletconfig—這是servletconfig接口的一個mock實作。在單元測試某種web元件(例如struts架構所提供的web元件)時,要求你設定由mockservletcontext所實作的servletconfig和servletcontext接口。

那麼,我們該如何使用這些mock對象呢?我們知道,httpservletrequest是一個持有描述http參數的固定值的元件,而正是這些參數驅動web元件的功能。mockhttpservletrequest,作為httpservletrequest接口的一個實作,允許你設定這些不可改變的參數。在典型的web元件測試情形下,你可以執行個體化這個對象并按如下方式設定其中的任何參數:

//指定表單方法和表單行為

同樣地,你可以執行個體化并全面地控制和分析httpresponse和httpsession對象。接下來,讓我們簡要觀察spring所提供的特定的junit架構擴充。

二、junit架構擴充

spring提供了下列一些特定的junit架構擴充:

abstractdependencyinjectionspringcontexttests—這是一個針對所有測試的超類,其具體使用依賴于spring上下文。

abstractspringcontexttests—這是一個針對所有的junit測試情形的超類。它使用一個spring上下文。并且,一般在測試中不是直接使用它,而是使用abstractdependencyinjectionspringcontexttests或者abstracttransactionalspringcontexttests這樣的派生類。

abstracttransactionalspringcontexttests—這是一個針對所有測試的超類,我們一般把它應用在事務相關的測試中。注意,一旦完成每個測試它就會正常地復原事務;而且你需要重載onsetupintransaction和onteardownintransaction方法以便手工開始并送出事務。

abstracttransactionaldatasourcespringcontexttests—這是abstracttransactionalspringcontexttests的一個子類,它使用了spring的基于jdbc的jdbctemplate工具類。

所有上面這些擴充将極大程度地簡化在測試時對于相關操作的依賴性注入和事務管理。

五、事務性單元測試

到目前為止,你已看到了相對簡單的junit測試—它僅發生在用mock對象支援的一個控制器的上下文中。但是,如果測試一個web元件隻有在一個事務性上下文(例如,通過依賴性注入與hibernate內建到一起)中才有意義的情況又會怎麼樣呢?不必擔心,spring mvc為junit架構提供了一個體面的擴充集合—它能準确地提供依賴性注入和事務安全測試(也就是,任何更新在測試完成後都将被復原)。

測試步驟:

讓我們看一種假想的情形—你要實作一個元件(例如mytransactionalcontroller)測試,該元件運作在一個事務性的上下文中(也即,其方法調用的結果發生在一個事務内并且它應該在測試運作完後被復原):

1.建立一個定制的junit類(mytransactionalcontrollertest),它擴充了spring的junit擴充類

2.為了實作從spring内置的單元測試中發現spring管理的bean,你需要重載getconfiglocations()方法并且傳回上下文檔案位置的string數組,請看如下:

3.擁有該類的一個測試屬性及其相關聯的getter和setter。由于abstracttransactionalspringcontexttests利用了auto-wiring(這是spring架構的一個特性—能夠根據類屬性的名字識别類依賴性并且用spring bean填入相比對的名字或id)技術而且在測試時它将自動地解決類的依賴性問題,是以在spring上下文檔案中該類屬性具有與spring管理的bean一樣的名字并且在測試時每個屬性都有一個适當命名的setter:

4.就象你通常操作“普通的”junit測試一樣實作測試方法:

注意,你是在調用可能會更新資料庫的方法submitpayment。spring的junit擴充(abstracttransactionalspringcontexttests)将在這個測試方法結束後實作自動復原。

5.如果你需要執行任何安裝或清除任務,則可以重載abstracttransactionalspringcontexttests的onsetupbeforetransaction()或onsetupintransaction()方法。abstracttransactionalspringcontexttests将重載從testcase繼承來的setup()和teardown()方法并且使其成為final類型。

繼續閱讀