當前位置:
首頁 > 最新 > Go 中的 5 種高級測試方法

Go 中的 5 種高級測試方法

首發於:https://studygolang.com/articles/12135

只要你寫過 Go 程序,肯定已經知道 Go 語言內置了一個功能完備的測試庫。在這篇文章中我們將列出幾種能幫助你提高編寫測試能力的策略。這些策略是我們在以往的編程經歷中總結出的可以很好的節省你的時間和精力的經驗。

使用測試套件(test suites)

測試套件是本篇文章中最重要的策略。它是一種針對擁有多個實現的通用介面的測試,在下面的例子中,你們將看到我是如何將 介面的不同實現傳遞進同一個測試函數,並且讓他們測試通過。

幸運的讀者可能已經接觸過使用該測試策略的代碼庫。在基於插件開發的系統經常能看到這種測試方式。針對介面的測試可以被用來驗證他的所有實現是是否滿足介面所需的行為。

測試套件能夠讓我們在面對一個介面多個實現的時候不用重複的為特定版本書寫測試,這會節省我們很多的時間。並且當你切換介面的底層實現代碼的時候,不用再寫額外的測試,就能保證程序的穩定性。

這裡有一個完整的例子。雖然這個例子都是相同的設計。你可以把它想像成一個是遠程資料庫(Mysql),一個是內存資料庫(sqlite)。

另外一個比較棒的例子就是 包。 當我們實現了自定義的 介面的時候,可以使用這個包()來直接驗證我們的實現是否滿足介面的要求,而不用自己重新設計測試代碼。

避免介面污染(interface pollution)

我們不能撇開介面談 Go 的測試。

介面在測試的上下文中十分重要,因為對於測試來講,介面是一種十分有力的工具,所以正確的使用他變得尤為重要。一個包經常會導出介面給用戶,用戶可以使用包中預定義的介面實現,也可以自己為該介面定義實現。

The bigger the interface, the weaker the abstraction.

Rob Pike, Go Proverbs

在導出一個介面的時候我們需要很謹慎的考慮是否應該導出它。開發者為了能讓用戶可以自定義介面的實現往往選擇導出這個介面。然而我們並不需要這樣,你只需要在你的結構體中實現了這個介面的行為,就可以在需要該介面的地方使用這個結構體。這樣,你的代碼包和用戶的代碼包就不會有強制的依賴關係。一個很好的例子就是 errors package ( 介面沒有被導出,但是我們可以在任何包中都可以定義自己的實現)。

如果你不想導出一個介面,那麼可以使用 internal/package subtree 來保證介面只有在包內可見。這樣我們就不用擔心用戶會依賴這個介面,也可以在新的需求出現的時候,十分靈活的修改這個介面。我們經常在使用一個外部依賴的時候創建介面,並使用依賴注入的方式把這個外部依賴作為這個介面的實現,這樣我們就可以排除外部依賴的因素而只測試自己的代碼。這讓用戶只需要封裝代碼庫中自己使用的那一小部分。

更多詳情,https://rakyll.org/interface-pollution/

不要導出並發原語

Go 提供了非常易於使用的並發原語,這也導致了它被過度的使用。我們主要擔心的是 和 package 。有的時候我們會導出一個 給用戶使用。另外一個常見的錯誤就是在使用 作為結構體欄位的時候沒有把它設置成私有。這並不總是很糟糕,不過在寫測試的時候卻需要考慮的更加全面。

當我們導出 的時候我們就為這個包的用戶帶來了測試上的一些不必要的麻煩。你每導出一個 就是在提高用戶在測試時候的難度。為了寫出正確的測試,用戶必須考慮這些:

請看下面這個在隊列中讀取數據的例子。這個庫導出了一個 用來讓用戶讀取他的數據。

現在有一個使用你的庫的用戶想寫一個測試程序。

用戶可能會認為使用依賴注入是一種好的方式。並且用下面的方式實現了自己的隊列:

這時有一個潛在的問題。

現在我們不知道如何來向這個 中插入數據,來模擬使用時這個代碼庫的真實運行情況,如果這個庫提供了一個同步的API介面,那麼我們可以並發的調用它,這樣測試就會非常的簡單。

當你有疑問的時候,一定要記住在用戶的包中使用 goroutine 是很簡單的事情,但是你的包一旦導出了就很難被移除。所以一定別忘了在 package 的文檔中註明這個包是不是多 goroutine 並發安全的。

有時,我們不可避免的需要導出一個 。為了減少這樣帶來的問題,你可以通過導出只讀的 或者只寫的 來替代直接導出一個 。

使用

包可以讓你在不用綁定埠或啟動一個伺服器的情況下測試你的 函數。這樣可以加速測試的速度,並且以一種很小的成本讓這些測試並行的運行。

下面是一個用 2 種方法實現同一個測試用例的例子。它們並不相似但是可以在測試時節省你很多的代碼和系統資源。

可能這種方式給你帶來的最大好處就是能讓你單獨測試你想測試的函數。沒有路由、中間件等等其他的影響因素。

想要了解更多,請看 post by Mark Berger。

使用單獨的 測試包

大多數情況下,測試都是寫在和 package 相同的包中並以 命名。而一個單獨的測試包就是將測試代碼和正式代碼分割在不同的包中。一般單獨的測試包都以包名+ 命名(例如: 包的測試包為 )。在測試包中你可以把需要測試的包和其他測試依賴的包一起導入進去。這種方式能讓測試更加的靈活。當遇到包中循環引用的情況,我們推薦這種變通的方式。他能防止你對代碼中易變部分進行測試。並且能讓開發者站在包的使用者的角度上來使用自己開發的包。如果你開發的包很難被使用,那麼他也肯定很難被測試。

這種測試方法通過限制易變的私有變數來避免容易發生改變的測試。如果你的代碼不能通過這種測試,那麼在使用的過程中肯定也會有問題。

這種測試方法也有助於避免循環的引用。大多數包都會依賴你在測試中所要用到的包,所以很容易發生循環依賴的情況。而這種單獨的測試包在原包,和被依賴包的層次之外,就不會出現循環依賴的問題。一個例子就是 包中實現了一個URL的解析器,這個解析器被 包所使用。但是當對 包進行測試的時候,就需要導入 包,因此 包產生了。

現在當你使用一個單獨的測試包的時候,包中的一些結構體或者函數由於包的可見性的原因在單獨的測試包中不能被訪問到。大部分人在基於時間的測試的時候都會遇到這種問題。針對這種問題,我們可以在包中 文件中將他們導出,這樣我們就可以正常的使用了。

記住這些事情

上面的這些方法並不是銀彈,但是在實際使用的過程中需要我們仔細的分析問題,並找到最適合的解決方案。

想要了解更多的測試方法?

你可以看看這些文章:

或者這些視頻:

via: https://segment.com/blog/5-advanced-testing-techniques-in-go/

作者:Alan Braithwaite

譯者:saberuster

校對:polaris1119

本文由 GCTT 原創編譯,Go 中文網 榮譽推出


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Go語言中文網 的精彩文章:

Go 系列教程—10.switch 語句

TAG:Go語言中文網 |