單元測試總結

 A、單元測試原理概括程序員

優良的單元測試具備如下的特色:簡稱爲 A-TRIP。web


自動性(Automatic)
spring

完備性(Thorough)數據庫

可重複性(Repeatable)數組

獨立性(Independent)安全

專業性(Professional)
下面讓咱們逐一理解它們的含義。服務器

自動性
單元測試是自動執行的,這裏的自動指兩個方面:1執行測試,2檢查測試結果 
執行測試應該是足夠簡單的,這樣,咱們就能夠隨時隨地的進行測試。所以,執行相應測試就應該像在IDE中點擊一個按鈕,或者在命令行中打一個命令那麼簡單。一些IDE甚至會在後臺連續的進行測試。 
維護這個環境很關鍵,不要引入哪些會破壞自動測試模型,須要手工進行干預的單元測試。若是測試須要一些環境(網絡,數據庫,等等),那就把它作爲測試的一部分。Mock對象能夠有效的隔離對外部的依賴。 
運行測試的不光你一我的,還應該有一臺機器對全部提交的代碼持續的運行全部的測試。這種自動、無人職守的檢查的做用就像一個定位杆,這種安全機制保證全部提交的東西不會破壞任何測試。理想狀況下並沒必要須這樣子,由於你能夠依靠每個開發人員都會自行運行全部必需的測試。回到現實,可能某個傢伙再某個遙遠的地方並無執行必需的測試。也許在他的機器上有一些代碼可以保證一切沒問題,可他們卻沒有提交代碼。這樣雖然在本地能夠執行,其餘地方就會出問題。 
最後,自動性的含義還有測試必須可以自行判斷成功仍是失敗。讓一我的(你或者其餘倒黴蛋)經過檢查測試產生的數據結果來判斷代碼的正確性,這是讓項目失敗不錯的方式。測試的自檢查是一致迴歸的重要特性。人類並不擅長這種重複性的工做,另外,在項目裏,咱們還有不少更重要的事情去作。 
測試的自動執行和自動檢查是很是關鍵的,這意味着你不用花太多的心思再這上面,它已經成爲了項目的一部分。測試是項目安全保護網的重要組成部分。它在你掉下來時接住你,且不會擋道,這樣你就能夠集中精力走"鋼絲"了。網絡

完全性
良好的測試應該是完全的,全部可能會出錯的地方都應該測到。如何可以作到這一點呢?一種極端狀況,測試每一行代碼、每個分支、每個拋出的異常,諸如此類;另外一個極端,只測試最可能的狀況:邊界條件、數據丟失、數據格式無效,等等。這就須要根據項目狀況進行區分了。 
若是追求較高的測試覆蓋度,那就須要尋求代碼覆蓋工具來幫忙了(例如:免費的nounit,quilt,商用的Clover),用這些工具能夠知道到底有多少代碼是被測試覆蓋的。 
有一個事實須要注意,Bug在代碼中的分佈狀況是不均勻的,而是喜歡彙集在有問題的地方(不少昆蟲都這樣,例如:蒼蠅)。 
這種現象引出了一段很是著名的呼聲「別打補丁,重寫」。一般,從頭重寫一段有一堆問題的代碼的代價和痛苦程度要低得多。固然,有了單元測試,從頭重寫代碼也安全多了,單元測試能夠保證新代碼可以按照預約的執行。app


可重複性
測試用例之間是獨立的同時,也應該是獨立於環境的。目標就是保持每一個測試可以以任意順序、不斷的重複執行,且獲得一樣的結果。這意味着測試不能依賴於不可控的任何外部環境。 
使用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)單元測試的優勢
一、它是一種驗證行爲。
    程序中的每一項功能都是測試來驗證它的正確性。它爲之後的開發提供支緩。就算是開發後期,咱們也能夠輕鬆的增長功能或更改程序結構,而不用擔憂這個過程當中會破壞重要的東西。並且它爲代碼的重構提供了保障。這樣,咱們就能夠更自由的對程序進行改進。
二、它是一種設計行爲。
    編寫單元測試將使咱們從調用者觀察、思考。特別是先寫測試(test-first),迫使咱們把程序設計成易於調用和可測試的,即迫使咱們解除軟件中的耦合。
三、它是一種編寫文檔的行爲。
    單元測試是一種無價的文檔,它是展現函數或類如何使用的最佳文檔。這份文檔是可編譯、可運行的,而且它保持最新,永遠與代碼同步。
四、它具備迴歸性。
    自動化的單元測試避免了代碼出現迴歸,編寫完成以後,能夠隨時隨地的快速運行測試。

(3)單元測試的範疇
    若是要給單元測試定義一個明確的範疇,指出哪些功能是屬於單元測試,這彷佛很難。但下面討論的四個問題,基本上能夠說明單元測試的範疇,單元測試所要作的工做。
一、 它的行爲和我指望的一致嗎?
    這是單元測試最根本的目的,咱們就是用單元測試的代碼來證實它所作的就是咱們所指望的。
二、 它的行爲一直和我指望的一致嗎?
    編寫單元測試,若是隻測試代碼的一條正確路徑,讓它正確走一遍,並不算是真正的完成。軟件開發是一個項複雜的工程,在測試某段代碼的行爲是否和你的指望一 致時,你須要確認:在任何狀況下,這段代碼是否都和你的指望一致;譬如參數很可疑、硬盤沒有剩餘空間、緩衝區溢出、網絡掉線的時候。
三、 我能夠依賴單元測試嗎?
    不能依賴的代碼是沒有多大用處的。既然單元測試是用來保證代碼的正確性,那麼單元測試也必定要值得依賴。
四、 單元測試說明個人意圖了嗎?
    單元測試可以幫咱們充分了解代碼的用法,從效果上而言,單元測試就像是能執行的文檔,說明了在你用各類條件調用代碼時,你所能指望這段代碼完成的功能。

[摘自]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)同樣容易。

1、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組件測試情形下,你能夠實例化這個對象並按以下方式設置其中的任何參數:
//指定表單方法和表單行爲

 

MockHttpServletRequest request = new MockHttpServletRequest("GET", "/main.app");
request.addParameter("choice", expanded);
request.addParameter("contextMenu", "left");

 

一樣地,你能夠實例化並全面地控制和分析HttpResponse和HttpSession對象。接下來,讓咱們簡要觀察Spring所提供的特定的JUnit框架擴展。

2、JUnit框架擴展

Spring提供了下列一些特定的JUnit框架擴展:

AbstractDependencyInjectionSpringContextTests—這是一個針對全部測試的超類,其具體使用依賴於Spring上下文。

AbstractSpringContextTests—這是一個針對全部的JUnit測試情形的超類。它使用一個Spring上下文。而且,通常在測試中不是直接使用它,而是使用AbstractDependencyInjectionSpringContextTests或者AbstractTransactionalSpringContextTests這樣的派生類。

AbstractTransactionalSpringContextTests—這是一個針對全部測試的超類,咱們通常把它應用在事務相關的測試中。注意,一旦完成每一個測試它就會正常地回滾事務;並且你須要重載onSetUpInTransaction和onTearDownInTransaction方法以便手工開始並提交事務。

AbstractTransactionalDataSourceSpringContextTests—這是AbstractTransactionalSpringContextTests的一個子類,它使用了Spring的基於JDBC的jdbcTemplate工具類。
全部上面這些擴展將極大程度地簡化在測試時對於相關操做的依賴性注入和事務管理。

5、事務性單元測試

到目前爲止,你已看到了相對簡單的JUnit測試—它僅發生在用mock對象支持的一個控制器的上下文中。可是,若是測試一個Web組件只有在一個事務性上下文(例如,經過依賴性注入與Hibernate集成到一塊兒)中才有意義的狀況又會怎麼樣呢?沒必要擔憂,Spring MVC爲JUnit框架提供了一個體面的擴展集合—它能準確地提供依賴性注入和事務安全測試(也就是,任何更新在測試完成後都將被回滾)。

測試步驟:

讓咱們看一種假想的情形—你要實現一個組件(例如MyTransactionalController)測試,該組件運行在一個事務性的上下文中(也即,其方法調用的結果發生在一個事務內而且它應該在測試運行完後被回滾):

1.建立一個定製的JUnit類(MyTransactionalControllerTest),它擴展了Spring的JUnit擴展類

AbstractTransactionalSpringContextTests:

import org.springframework.test.AbstractDependencyInjectionSpringContextTests;

public class MyTransactualControllerTest extends

AbstractTransactionalSpringContextTests {

public class.

 

2.爲了實現從Spring內置的單元測試中發現Spring管理的bean,你須要重載getConfigLocations()方法而且返回上下文文件位置的String數組,請看以下:

 

protected abstract String[] getConfigLocations(){

return new String[] {"classpath:/test/spring-context.xml"};

}

 

3.擁有該類的一個測試屬性及其相關聯的getter和setter。因爲AbstractTransactionalSpringContextTests利用了auto-wiring(這是Spring框架的一個特性—可以根據類屬性的名字識別類依賴性而且用Spring bean填入相匹配的名字或ID)技術並且在測試時它將自動地解決類的依賴性問題,因此在Spring上下文文件中該類屬性具備與Spring管理的bean同樣的名字而且在測試時每一個屬性都有一個適當命名的setter:

 

public MyTransactualController myTransactualController;

/**

* @返回myTransactualController。

*/

public MyTransactualController getMyTransactualController() {

return this.myTransactualController;

}

/**

*@參數myTransactualController。

*/

public void setMyTransactualController(

MyTransactualController myTransactualController) {

this.myTransactualController = myTransactualController;

}

 

4.就象你一般操做「普通的」JUnit測試同樣實現測試方法:

 

public void testCorrectBehavior() throws Exception{

//運行該事務性方法

myTransactualController.submitPayment( new Payment( 100 ) );

assertTrue( myTransactualController.isValid() );

}

 

注意,你是在調用可能會更新數據庫的方法submitPayment。Spring的JUnit擴展(AbstractTransactionalSpringContextTests)將在這個測試方法結束後實現自動回滾。

5.若是你須要執行任何安裝或清除任務,則能夠重載AbstractTransactionalSpringContextTests的onSetUpBeforeTransaction()或onSetUpInTransaction()方法。AbstractTransactionalSpringContextTests將重載從TestCase繼承來的setUp()和tearDown()方法而且使其成爲final類型。

相關文章
相關標籤/搜索