什么是單元測試?

  一直以來大家有一種錯誤的理念,總是認(rèn)為單元測試作為一種測試方法,主要是由測試工程師來主導(dǎo)的一種測試手段。并非如此,借用教科書上的說法,單元測試作為一種最基礎(chǔ)的測試手段,其主要作用是“在計算機編程中,針對程序模塊(最軟件設(shè)計中的最小單元)進(jìn)行正確性檢驗的測試工作。”

  之前曾經(jīng)閱讀過一篇文章,作者提了一個例子,他說他兒子在使用樂高積木做玩偶過程中,組裝完一些零部件之后,就會試圖去測試一下部件是否能夠正常運轉(zhuǎn),作者說這種過程實際上就是一種單元測試。他認(rèn)為,單元測試的思想其實普遍存在于人類文明的發(fā)展過程中,從人類開始制作工具來說,就需要使用單元測試的方法對工具的可用性進(jìn)行測試。尤其是進(jìn)入工業(yè)時代以來,傳統(tǒng)制造業(yè)尤其重視產(chǎn)品質(zhì)量,為了提高產(chǎn)品質(zhì)量,采取了一系列措施,這種例子其實不勝枚舉。

  去年我曾經(jīng)有幸為一家國企開發(fā)過一些質(zhì)量管理系統(tǒng),并深入了解了這些企業(yè)的質(zhì)量管理流程,雖然從宏觀層面來看,以互聯(lián)網(wǎng)經(jīng)濟為代表的新興經(jīng)濟對以包括制造業(yè)在內(nèi)的實體經(jīng)濟帶來了許多沖擊,但是依然那些優(yōu)秀的制造業(yè)企業(yè)依然堅定的將產(chǎn)品質(zhì)量作為核心競爭力,并通過互聯(lián)網(wǎng)的這種模式,讓企業(yè)的發(fā)展獲得了新的機遇。

  軟件行業(yè)其實本質(zhì)上和制造業(yè)沒有那么巨大的區(qū)別,以軟件開發(fā)過程中,由軟件工程師們開發(fā)出來的每一個方法實際上就是開發(fā)者們解決虛擬世界生存問題的零部件,每一個產(chǎn)品或應(yīng)用就是這樣的工具。在一個軟件開發(fā)過程中,往往需要涉及到多人操作,經(jīng)常需要調(diào)用別人編寫的模塊代碼。尤其是在面向互聯(lián)網(wǎng)開發(fā)中,如果不能認(rèn)真的對待每一行代碼,就有可能一些疏忽大意造成不可彌補的后果。

  因此做好單元測試,不僅僅只是開發(fā)者們應(yīng)該采用的一種測試手段,而是應(yīng)該是一種基本的技術(shù)手段,對我們開發(fā)的每一行代碼,都應(yīng)該使用單元測試進(jìn)行覆蓋,自己負(fù)責(zé)的模塊定義應(yīng)該盡量明確,模塊內(nèi)部的更改不會影響他模塊,讓模塊的質(zhì)量得到穩(wěn)定和良好的保證。

  為什么大家認(rèn)為單元測試不合時宜?

  當(dāng)然,大部分開發(fā)者也許明白這個道理,但是卻認(rèn)為單元測試不合時宜,主要包括以下幾點:

  1,沒有時間

  ---- 在軟件開發(fā)過程中,往往以完成任務(wù)為第一要務(wù),而由于時間計劃的安排,單元測試的設(shè)計和實踐均需要花費大量的時間完成,過度的使用單元測試會拖延項目的進(jìn)度,而且花費的邊界成本高昂而收效甚微。

  ---- 筆者認(rèn)為,跟隨代碼一起,設(shè)計和應(yīng)用單元測試,并不會顯著的帶來時間上的損害,反而會讓開發(fā)者更加關(guān)注代碼本身的目的,讓輸入輸出和計算過程更為合理。

  2,計劃會變化:需求變化快,單元測試跟不上需求的變化

  由于軟件產(chǎn)品需求的不確定性,導(dǎo)致之前設(shè)計的單元測試并非符合項目的實際功能性需求,沒必要花費這個精力設(shè)計這種沒用的場景。

  ----筆者認(rèn)為,需求變化與是否應(yīng)用單元測試關(guān)系不大。

  3,單元測試是用來找Bug的,應(yīng)該有測試來編寫

  單元測試作為一種測試手段,是為了發(fā)現(xiàn)代碼中存在的bug。

  --- 筆者認(rèn)為:單元測試的目的是為了讓程序作者更加快捷的發(fā)現(xiàn)代碼中存在的問題,并在第一時間發(fā)現(xiàn)和解決,從而保障軟件質(zhì)量。

  4,有程序員定義的單元測試沒有意義,因為程序員設(shè)定的邏輯場景就有可能不準(zhǔn)確

  單元測試作為由程序員根據(jù)實際用例出發(fā)設(shè)計的測試流程,本身可能并非符合業(yè)務(wù)的實際應(yīng)用需要,因此單元測試應(yīng)該由最了解需求的人來進(jìn)行設(shè)計。而且完成單元測試設(shè)計后,也需要有了解需求的人對單元測試用例和代碼進(jìn)行審查。

  --- 筆者認(rèn)為:由程序員開發(fā)的單元測試沒有意義,那么他寫的代碼呢?寫代碼本身就是為了完成指定的用例場景,如果單元測試都不合理,那么如何確保代碼本身就合理?

  5、TDD測試驅(qū)動,不過是一種形式主義,本身就不合時宜

  在TDD模式中,測試先于開發(fā),所以需要經(jīng)過良好的設(shè)計和定義,最好能夠解耦合各個模塊,讓代碼更加完美的匹配測試代碼。但是這種代碼本身就要求經(jīng)驗豐富的開發(fā)者才能完成,而能夠完成這種能力的,本身就屬于優(yōu)秀開發(fā)者,并不需要設(shè)計單元測試代碼。

  -- 筆者認(rèn)為:優(yōu)秀的開發(fā)者,本身不僅僅只是因為他們出身優(yōu)秀,而是因為他們掌握了包括單元測試等在內(nèi)的優(yōu)秀學(xué)習(xí)方法并每天都在實踐。

  好的單元測試的標(biāo)準(zhǔn)

  筆者最近閱讀《構(gòu)建之美》(第一版)深刻認(rèn)同提出的關(guān)于好的單元測試的標(biāo)準(zhǔn),例如以下幾點:

  1、單元測試應(yīng)該在最低的功能參數(shù)上驗證程序的正確性。

  2、單元測試應(yīng)該由最熟悉代碼的人(程序的作者)來寫。

  3、單元測試過后,機器狀態(tài)保持不變。---筆者認(rèn)為,不僅僅機器的狀態(tài)應(yīng)該保持不變,數(shù)據(jù)中存儲的數(shù)據(jù)關(guān)系也應(yīng)該保持不變,例如,通過單元測試錄入的數(shù)據(jù),應(yīng)該在完成測試后自動回滾,或者添加測試戳記后,手動清除。

  4、單元測試要快(一個測試的運行時間是幾秒鐘,而不是幾分鐘)。

  5、單元測試應(yīng)該產(chǎn)生可重復(fù)、一致的結(jié)果。

  6、單元測試具有獨立性的特點,單元測試的運行、通過、失敗不依賴于別的測試,可以認(rèn)為構(gòu)造數(shù)據(jù),以保持單元測試的獨立性。

  7、單元測試應(yīng)該負(fù)載所有的代碼路徑。

  8、單元測試應(yīng)該繼承到自動測試的框架中。

  9、單元測試必須與產(chǎn)品代碼一起保存和維護。

  何妨不試一試呢?

  在軟件企業(yè)的發(fā)展前期,往往會由于技術(shù)上的一些偶發(fā)性突破,取得短期的優(yōu)勢,但是隨著企業(yè)的規(guī)模發(fā)展,使用更加規(guī)范化甚至成為形式化的方式確保產(chǎn)品的質(zhì)量是必然趨勢,而單元測試則是一種看似簡單,但卻效果顯著的手段,相信通過一段時間的實踐,就會深刻的領(lǐng)悟到其中的妙處。