淺談單元測試

姓名:鄒宇翔? ? ? 學號:16019110051

轉(zhuǎn)載自:

http://mp.weixin.qq.com/s/Zs1GnIKyoIJUFUeEQCRijw

【嵌牛導讀】大家都知道程序編好后會經(jīng)過QA的檢測,同時正因為如此大家往往忽略了單元測試的重要性

【嵌牛鼻子】單元測試(UT)

【嵌牛提問】那么應該如何去進行單元測試呢?它對于自己的程序有什么好處呢?

【嵌牛正文】

什么是 UT ?

UT ( Unit Test )即單元測試。

UT 有什么價值?

大部分的開發(fā)都不喜歡寫 UT,原因無非以下幾點:

產(chǎn)品經(jīng)理天天催進度,哪有時間寫 UT。

UT 是測試自己的代碼,自測?那要 QA 何用?

自測能測出 bug ?都是基于自身思維,就像考試做完第一遍,第二遍檢查一樣,基本檢查不出什么東西。

UT 維護成本太高,投入產(chǎn)出比太低。

不會寫 UT。

總之有無數(shù)種理由不想寫 UT,作為工作不到三年的菜鳥深有體會。之前在點評工作的時候,團隊的“UT”都集中于 RPC 的服務端。為啥帶雙引號?

因為 RPC 的服務端沒有頁面可以功能測試,部署到測試環(huán)境測試太麻煩,只能寫 UT 了。在這個場景下我認為叫”驗證”更合適,驗證不等于測試。

驗證往往只寫主邏輯是否通過,且就一個 Case,且沒有 Assert,有的是 System.out。

本人實習的時候做測試的,那時候知道一個測試模型。如下圖:

(圖一)

圖的意思就是越底層做的測試效果越好,越往上則越差。也就是說大部分公司現(xiàn)在做的功能測試其實是效果最差的一種測試方式。

另外, QA 界有個現(xiàn)場:大家都知道功能測試沒技術含量,那如何使自己突出呢?答案就是:自動化測試?,F(xiàn)實是沒幾個公司能做好自動化測試,業(yè)界做的比較好的百度算一個。那么為啥自動化測試這么難做的?在這個模型當中,越往上黑盒越大,自動化測試難度就越大。

這句話反過來就是越往下自動化測試就越好做?沒錯, UT 其實是最容易實現(xiàn)且效果最好的自動化測試。 所以在很多公司出現(xiàn)一種現(xiàn)場: QA 寫 UT。

原因總結(jié)一下就兩點:開發(fā)不愿意寫 UT,QA 想自動化測試解放自己。

以上的模型只是理論上說明 UT 具有巨大的價值,但是真的如此么?我只想說,只有真正嘗到 UT 的好處的甜頭才會意識到 UT 的價值。

Unit Test & Intergration Test

單元測試和集成測試的界線我相信大部分開發(fā)也是不清晰的。個人理解單元測試針對于一塊業(yè)務邏輯最小的單元,太抽象。物理上可以簡單理解為一個類的方法,可以是 public 方法也可以是 private 方法。一個單元測試不應該包含外部依賴的邏輯,反之就是集成測試了。

問題的核心就在于此。一個 service 的一個接口實現(xiàn)可能依賴很多第三方:1.本地其它的 service 2. dao 調(diào)用 3. rpc 調(diào)用 4.微服務調(diào)用。如下圖:

(圖二 )

也就是說你的單元測試,真正調(diào)用了外部依賴那就是集成測試。這其實很常見對不?我們先說這種情況下如何集成測試。

Local Integration? Test

本地集成測試也就是說不依賴與其他進程。包括: service 依賴其他本地 service 或者 dao 的情況。在講述如何集成測試之前,我們先理一下測試模型,測試主要包含三塊內(nèi)容:1.數(shù)據(jù)準備 2.執(zhí)行邏輯 3.輸出驗證。

第一步:數(shù)據(jù)準備

在本地集成測試里,數(shù)據(jù)來源基本上來自于 dao , dao 來自于 sql 。也就是在執(zhí)行一個 case 之前,執(zhí)行一些 sql 腳本,數(shù)據(jù)庫則使用 h2 這類 memory database ,切記不要依賴公司測試環(huán)境的 db 。

下圖是使用 spring - test 框架的一個 case ,可以在 case 執(zhí)行之前準備我們所需要的各種數(shù)據(jù),另外在執(zhí)行完 case 之后,執(zhí)行 clean.sql 腳本來清理臟數(shù)據(jù)。這里也說明一個 case 的執(zhí)行環(huán)境是完全獨立的, case 之間互不干擾,這很重要。

(圖三)

第二步:執(zhí)行邏輯最簡單,就是調(diào)用一下我們測試的方法即可

第三步:驗證

集成測試一般是調(diào)用 service ,或者 dao 的接口驗證。

舉個例子: CRUD 操作的集成測試

調(diào)用 C 接口

調(diào)用 R 接口,驗證 C 成功

調(diào)用 U 接口

調(diào)用 R 接口,驗證 U 成功

調(diào)用 D 接口

調(diào)用 R 接口,驗證 D 成功

Remote Integration? Test

假設我們一個 service 實現(xiàn)依賴某個 RPC Service

第一步:數(shù)據(jù)準備

跑到別人家的數(shù)據(jù)庫插幾條數(shù)據(jù)?或者跟 PRC Service 的 Owner 商量好,搭一個測試環(huán)境供我們測試?有些公司還真有專門的自動化測試環(huán)境,那么即使有測試環(huán)境,那如何實現(xiàn)各種 case 場景下,第三方 Service 很配合的返回數(shù)據(jù)給我們?想想都蛋疼。

第二步:執(zhí)行方法

假設我們成功的解決了第一步中的問題,皆大歡喜?,F(xiàn)在來看第二步,假設我們的 service 里面調(diào)用了另一個 RPC Service 創(chuàng)建了很多數(shù)據(jù),跑了無數(shù)次 case ,結(jié)果?!?RPC Service 對應的數(shù)據(jù)庫都是我們的臟數(shù)據(jù),如何清理?而且他們敢隨便刪數(shù)據(jù)嗎?想想也蛋疼。

第三步:輸出驗證

假設我們又愉快的解決了第二步中的問題?,F(xiàn)在來看第三步,假設我們的方法執(zhí)行最終輸出是創(chuàng)建了一個訂單,訂單當然是調(diào)用訂單 Service 接口了,那么我們?nèi)绾悟炞C訂單是否成功創(chuàng)建了呢?或許可以調(diào)用訂單 Service 查詢訂單的接口來驗證。很明顯大多數(shù)情況下并沒有這么完美。想想也蛋疼呀。

通過以上分析, Local Integration? Test 是可行的, Remote Integration? Test 基本不可行。

那么有沒有什么辦法解決呢?答案就是 Mock

第一步: Mock RPC? Service 想返回什么數(shù)據(jù)就返回什么數(shù)據(jù)

第二步:還是 Mock 接口,想調(diào)用幾次就調(diào)用幾次

第三步:這一步等到下面講完單元測試就明白了

Unit Test

上面我們談到 Mock 可以解決外部依賴的問題,現(xiàn)在有很多 Mock 的開源框架比如: mockito。那么問題來了,既然我們可以 mock 第三方遠程依賴,為何不 mock dao、local service 呢?沒錯外部依賴全部 mock 掉,就是單元測試了。因為我們只關心所測試的方法的業(yè)務邏輯,也就是真正高內(nèi)聚的邏輯單元了。如下圖:

(圖四)

好處如下:

沒有什么數(shù)據(jù)是造不出來的,通通返回 Mock 的對象

代碼中的異常處理代碼,也可以通過 mock 接口,使之拋出異常

不產(chǎn)生任何臟數(shù)據(jù)

跑 case 更快了,因為不用啟動整個項目,相當于 Main 方法

有人會說,都 mock 了還測試個蛋蛋。

這就是對于單元測試的理解了,單元測試應該只針對于目標方法的業(yè)務邏輯測試, dao 、其它 service 應該在它們自身的單元測試去測試。對于依賴的第三方,我們應該信任它們能正確的完成我們所預期的。這句話很難理解對不對?

舉幾個例子

例子一:方法的最后是執(zhí)行 dao 的 create 操作,那么該如何驗證?

我們應該驗證的內(nèi)容是:

dao 的 create 方法被調(diào)用了

調(diào)用次數(shù)是對的

調(diào)用參數(shù)也是對的

沒錯,只要這三個驗證通過,那么這個 case 執(zhí)行就是通過的。因為我們相信 dao 的 create 操作能正確的完成我們所預期的,只要我們調(diào)用了正確的次數(shù)并且參數(shù)都是對的。 dao 的執(zhí)行的正確性保證是在該 dao 的單元測試做的。

在 Remote Integration Test 里面第三步驗證道理是一樣的,我們應該驗證 RPC 接口被調(diào)用了且次數(shù)和參數(shù)都是對的,那么我們的 case 就算通過了,至于, RPC 服務端是否正確執(zhí)行是它們的事情不是我們所關心的。

Mockito 框架的 verify 接口就是做這件事情的。如果你理解了上述內(nèi)容,那么你就開竅了, UT 不在變得這么難寫。

什么時候用單元測試,什么時候用集成測試?

在本人的實踐中摸索發(fā)現(xiàn),對于簡單的業(yè)務,比如 crud 型的瘦 service,比較適合于集成測試。

以下情況適合于單元測試:

Util 類

含有遠程調(diào)用的方法

輸入少,業(yè)務邏輯復雜的方法

需要異常處理的方法

case 細到什么程度為好?

這個問題也是比較經(jīng)典的,一個方法要是所有的路徑都覆蓋到,那么要寫很多的 case,說真的累死人。我的建議是兩個原則:

1. 核心邏輯,容易出錯的邏輯一定要覆蓋到;

2. 根據(jù)自己的時間。 沒必要寫的非常多,畢竟 case 維護成本很高,業(yè)務邏輯一改, case 得跟著改。

總結(jié)

本人目前在從事于開源項目(Apollo(配置中心)? )研發(fā),開源項目對代碼質(zhì)量要求相對來說高一些, UT 當然是很重要的一環(huán)。剛開始也不會寫 UT,當然態(tài)度上也不重視 UT。

老大的代碼 UT 覆蓋率很高,抱著對開源負責的態(tài)度慢慢接受學習 UT ,到后來嘗了幾次甜頭后,發(fā)現(xiàn) UT 真的很實用,價值也很高,但是很遺憾 UT 被大部分開發(fā)所忽略。當然本人對 UT 的理解、實踐還不夠,仍需繼續(xù)實踐模式。

最后說一句:當開發(fā)完功能,跑完 UT,你可以放心的上線了的時候,你的 UT 就成功了。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 本文作者:張樂。全文約 4199 字,讀完可能需要 7 分鐘。雖然這篇不是以 Python 為示例的,但基本的思路...
    羅義的夏天閱讀 966評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • Android單元測試介紹 處于高速迭代開發(fā)中的Android項目往往需要除黑盒測試外更加可靠的質(zhì)量保障,這正是單...
    東經(jīng)315度閱讀 3,420評論 6 37
  • 那場雨從未停過,落在那年,落在小城,烙下印記在我心間。 我空有滿腹詩文,卻難以表達我的心。 “小生寒窗苦讀十余載,...
    柯望一閱讀 10,047評論 3 8
  • 01 十月二十二日晚上聽了笑笑梨老師的課,讓我受益匪淺??!雖然教室很小,但是我感受到了課堂的氛圍。來上課的同學...
    寫作星閱讀 710評論 3 3

友情鏈接更多精彩內(nèi)容