Go 語言編寫單元測試

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

吾嘗終日而思矣,不如須臾之所學也;吾嘗跂而望矣,不如登高之博見也。登高而招,臂非加長也,而見者遠;順風而呼,聲非加疾也,而聞者彰。假輿馬者,非利足也,而致千裡;假舟楫者,非能水也,而絕江河。君子生非異也,善假於物也。

-- 荀況 《勸學》

Go 語言對於單元測試是很重視的,且不說其他的作者的背景啊,開源庫啊,第三方的支援之類的,有兩點讓我對 Go 語言關於單元測試的重視程度的有信心的點在於:

  1. Go 語言原始碼和內建庫自身的單元測試完備性
  2. Go 語言內建單元測試命令

從這兩點,我認為測試在 Go 語言中具有非常重要的地位,所以在這篇文章中,我也嘗試講一些關於 Go 語言單元測試的東西。

編寫 Go 單元測試代碼

Go 的測試方法看上去相對比較低級,它依賴於命令 go test 和一些能用 go test 啟動並執行測試函數的編寫約定。但是,我認為這就是所謂的 Go 風格,用 Go 以來,我的感受是 Go 語言就是保持了 C 語言編程習慣的一門語言。

首先,為了開始這篇文章,我寫寫一個簡單的函數用作後面要測試的例子,但是,考慮到後面可能要講一些稍微複雜一點的內容,所以,這個例子我留有一些可以改變的地方,大家可以選擇著看:

這個例子就是這麼簡單,將這個檔案命名為 main.go,然後我們就應該編寫測試代碼了。測試代碼的檔案放置的位置可以隨意,package 也可以隨意寫,但是,檔案名稱必須以 _test 結尾,所以,我這裡就命名為 main_test.go

這裡編寫測試函數,有幾個需要注意的點:

  1. 每個測試檔案必須以 _test.go 結尾,不然 go test 不能發現測試檔案
  2. 每個測試檔案必須匯入 testing
  3. 功能測試函數必須以 Test 開頭,然後一般接測試函數的名字,這個不強求

根據這些條件,我們可以寫出一個測試檔案:

測試檔案寫完之後,我們就應該執行測試了,開啟命令列工具,敲入這條命令:go test main_test.go main.go -v -cover

然後就應該等待測試結果了,這裡加了兩個參數,分別是 -v-cover,如果不加上的話你會發現只有 Test Pass 的簡單提示,而看不到我們加了參數的具體提示:

=== RUN   TestAdd--- PASS: TestAdd (0.00s)PASScoverage: 50.0% of statementsok      command-line-arguments  0.008s

基於表的測試方式

在 Go 語言中,有一種常用的測試套路,叫做基於表的測試方式,其核心就是我們需要針對不同的情境,其實也就是不同的輸入和輸出來驗證一個功能。例如我們要驗證的 Add 函數,我們需要驗證的功能點有很多,例如:

  • 兩個正數相加是否正確
  • 兩個負數相加是否正確
  • 一個正數加上一個負數是否正確
  • 有一個數為 0 是否正確

那麼,我們就可以使用 基於表的測試方式 了,代碼可以這樣寫:

Mock 依賴

前面介紹的測試都是比較簡單的,功能簡單的話我們就可以直接給定輸入,然後看輸出是否符合預期,這樣就可以很簡單得寫完單元測試了。但是,有的時候,由於商務邏輯的複雜性,功能代碼並不會就這麼直接,往往還會摻雜很多其他組件,這就給我們的測試工作帶來很大的麻煩,我這裡列舉幾個常見的依賴:

  • 組件依賴
  • 函數依賴

組件依賴和函數依賴是兩種比較常見的依賴,但是,這兩種依賴也是可以擴充開來說的,既可能來自於我們自己編寫的組件/函數,也可能是引入其他人寫的。但是,無妨,對於這些情況,我們都會做一些分析。

組件依賴處理

使用 Go 語言開發項目的時候,我們應該經常會抽離組件模組的,既然抽離了組件和模組,那麼久離不開組件的依賴了,既然有依賴,在測試的時候我們很多時候都是希望屏蔽掉相依元件的影響,從而更好得測試現有代碼的細節;或者說,我們希望根據自己的測試目標,控制相依元件的行為。但是,如果我們還想簡單得通過控制輸入和輸出來控制相依元件的行為,這個難度還是比較大的,所以,在這種情況下,我們一般會考慮傳一個 Stub 組件進入,從而達到控制相依元件行為的效果。

舉一個例子先,例如我們比較常見的 Service 層和 DAO 層的操作,Service 處理完邏輯之後,交給 DAO 層進行持久化,或者需要調用 DAO 層從持久化中擷取一些必要的資料;在測試的時候,我們很多時候不希望真的持久化或者從持久化中擷取資料,那麼就會對 DAO 層進行一些 Mock。首先,先給出一個啟動並執行例子:

這裡我們想要測試 Service 的正確性,但是又不想要真的持久化 DAO,所以,這個時候我們會自己建立一個 Stub,然後提供給 Service,同時,我們還能操作 DAO 的行為,達到運行得效果,例如:

這樣我們就能測試到登入成功的代碼了,登入成功的測試了,我們還希望測試一下登入失敗的,那麼也很簡單,修改一下就可以了唄:

這裡對測試代碼稍微改了一下,可以發現,我們可以通過修改一個變數來控制 Stub 的輸出,從而達到測試不同功能的效果,這就解決了組件依賴的問題。

函數依賴

函數依賴相比於組件依賴會更麻煩一點,因為我們在前面可以看到,組件依賴的話我們可以傳遞 Stub 進行,這樣我們可以隨意得控制 Stub 的行為,但是函數不行呀,這裡我們又不能傳函數進去,因為函數是被 import 進去的啊。問題就在這了,因為函數是被 import 進去的,所以可以理解為函數是全域的了,既然這樣,那麼我們為什麼不修改一下函數呢?什麼意思?我們先來看著正常的業務例子:

這裡我是想表達的一個意思就是需要先登入,然後登入完之後我們才能回複訊息,這裡我們的登入邏輯是簡單的,但是,在實際業務中可能這裡的登入邏輯就設計到 DB 訪問等等,我們希望不走真實的邏輯,而是自己來控制 Login 的行為。

首先,先分析一下我們的 UT 目的,我們的目的是測試 Reply 函數,我們期望是 Login 成功,那麼 Reply 也應該是成功的;如果 Login 失敗,那麼 Reply 也應該是失敗的。這個測試結論不應該被 Login 所影響,及時以後 Login 邏輯修改了,我們也應該是這個邏輯,不會受到影響,那麼我們可以這麼編寫 UT:

這裡可以發現,我們是修改了 Login 這個函數的代碼,從而控制 Login 函數的傳回值,這樣我們就可以測試我們寫的代碼的邏輯是否正確了。

可能有心細的同學發現,第二個函數依賴裡面的代碼有個特別的地方,在於函數 Login 是個變數,為什麼要用函數變數呢,不能直接使用函數嗎?這裡確實是一個麻煩得地方,因為如果使用直接函數的話,我們沒得賦值,那麼也就無法修改它了。如果代碼不是我們自己寫的,而是使用的其他同事的代碼,那麼問題就大了。這種情況下,我們可以怎麼處理呢?

在 Reference 的中有一篇文章:Mocking functions in Go 介紹了一種方法,那就是將我們引用的函數賦值為函數變數再使用,從而達到同樣的效果。

第三方庫

在前面的介紹中,我們都是自己重新寫了一個 Stub 類,但是同時問題暴露了,寫起來比較複雜和繁瑣,所以就有了一些第三方庫可以方便我們編寫這些東西,Gomock 就是一個,這裡就簡單示範一下 GoMock 的一些功能。

gomock 有兩個組件,分別是 gomockmockgenmockgen 可以根據我們的 interface 產生對應的 Stub 對象,例如我們前面提到的 DAO,因為我們 DAO 的 Interface 已經寫好了,所以我們可以很方便得產生 StubDao,只需要使用命令:

$ mockgen -source=main.go > mock_dao.go

然後我們查看一下 mock_dao.go 的內容:

可以發現兩個函數都被實現了 ReadAllSaveData,但是,同時我們也發現,產生的 DAO 比我們預期的要複雜得多,至於為什麼藥這麼複雜,看看接下來的使用就明白了。現在我們已經有了 DAO,那麼下一步就是控制 DAO 的輸出了。

這裡可以看到,使用 gomock 可以讓我們的控制輸出更加簡便,之前我們需要通過控制變數的方式來達到控制輸出的目的,但是這裡可以很方便得使用:

d.EXPECT().FuncXXX.Return(xxxx)

來指定函數應該返回什麼結果,確實方便了,同時,還省去了我們自己編寫 MockClass 的時間,這個還是值得一試的。

總結

OK,本文關於 Go 的 UT 就差不多這麼多了,這裡介紹了 Go 單元測試的編寫套路,以及介紹了我們在項目中常見的一些情境的處理,最後再介紹了一款第三庫用於加快 UT 的編寫。在我另一篇文章:從開源項目看 Python 單元測試 中我曾經提到,UT 是我們都希望別人寫,但是自己又不想寫的東西,希望,Go 語言相對簡單的編寫套路和模式能夠讓大家對 UT 有所重視,並且願意寫起來。lol。

Reference

  1. Go程式設計語言
  2. Mocking functions in Go
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.