天天看點

沒經過這些測試,你的微服務架構也敢進入生産環境?

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

微服務架構是指将應用程式拆分為一系列較小、且直接用于解決具體問題的元件的實踐方案。以此為基礎,架構中的每一個元件都将通過各類正常協定(例如 HTTP 或者更輕量化的 TCP)互相通信。

說到這裡,大家可能會好奇,對于微服務架構來說,測試真的很重要嗎?

答案當然是重要!測試的重要性是展現在多方面的,不過比較重要的是以下幾點:

  • 節約金錢與時間
  • 更安全
  • 強化生産品質(減少 bug 與錯誤數量)
  • 提升客戶滿意度
  • 最重要的是,夜裡能睡得更安穩

随時出 Bug、動不動就當機的應用程式,沒人會喜歡,而且往往這種應用程式的安全漏洞很多,如果黑客想從中竊取憑證或者搶劫資金,簡直易如反掌。如果我們想要開發一款具備一定複雜性的應用程式,那麼測試是一定需要的。

使用什麼測試方法?

目前軟體測試的種類比較多,大緻可以分為功能測試和非功能測試兩大類。其中功能測試類包括單元測試、內建測試、通煙測試、回歸測試、健全測試、Beta/ 驗收測試和端到端(e2e)測試,而非功能測試則包括了性能測試、負載測試、壓力測試、安全測試、合規測試和可用性測試。

一般來說,應用程式的複雜度越高,需要使用的測試類型也就越多。不過,有幾個測試是所有應用程式都不可或缺的:

  • 單元測試
  • 內建測試
  • E2E 測試、回歸測試與安全測試相結合

整體流程應該是,先編寫程式來檢查應用中各個層面是否在按照預期設計運作,若應用已經上線,那麼就需要進一步編寫測試來檢查代碼更新是否會對原有功能造成破壞。如果是微服務架構,那麼除了以上的基礎測試之外,可能還需要編寫專門的測試,例如負載測試,用于檢查系統在正常與預期峰值負載條件下的運作狀況。

少說話,多編碼

接下來,我們一起探讨一下如何在微服務架構當中實作上述基礎軟體測試類型。微服務架構使用 TCP 協定實作元件間的通信,并利用 Nest Framework 以 Node.JS 編寫而成。

很多人可能不太了解 NestJS,我們先簡單介紹一下,官方 GitHub repo 是這樣描述 Nest 的:

Nest 是一款架構,用于建構高效且可擴充的 Node.js 伺服器端應用程式。它利用現代 JavaScript 的特性,由 TypeScript 建構而成(保留純 JavaScript 相容性),同時結合有 OOP(面向對象程式設計)、FP(函數程式設計)以及 FRP(函數響應式程式設計)的元素。從底層來看,Nest 不僅能夠使用 Express,同時也相容其他多種庫,包括 Fastify,旨在輕松使用大量現有第三方插件。”

在示例中,我們将使用一個簡單的子產品 name:user 配合一個簡單函數 createUser,在資料庫内建立一個新使用者。

該子產品的檔案夾結構如下所示:

沒經過這些測試,你的微服務架構也敢進入生産環境?

我們設了一個監聽 create_user 消息的控制器。在利用 ValidationPipe 進行驗證之後,它會在服務之内調用一個具有相同名稱的函數。

沒經過這些測試,你的微服務架構也敢進入生産環境?

在服務之内,我們會對使用者密碼進行哈希處理。接下來,使用 TypeORM 将新使用者儲存在資料庫内。

沒經過這些測試,你的微服務架構也敢進入生産環境?

對這個子產品,我們使用 TypeORM 作為連結至表 User 的 ORM;同時,利用名為 UtilsModule 的另一個子產品實作某些輔助功能:

沒經過這些測試,你的微服務架構也敢進入生産環境?
沒經過這些測試,你的微服務架構也敢進入生産環境?

所謂單元,是指應用程式當中的最小可測試部分,例如函數、類或者過程。單元測試則代表一種軟體測試方法,旨在測試源代碼中的各個單元,以确定其是否符合開發階段的預期設計。

編寫單元測試,是為了保證不同代碼形式(函數、類等)的每個簡單實作,均符合設計、要求并能夠按預期運作。

單元測試的目标,在于隔離程式中的各個部分,并測試這些部分是否正常工作。

換言之,與目前測試單元無關的其他代碼部分,則以模拟形式存在,僅作為運作環境使用。

在我們的示例中,需要測試的單元自然就是之前提到的 createUser 了。這意味着我們首先得把它跟其他元件隔離開來。是以,第一步就是模拟 user repository 類,這個類代表着資料庫使用 TypeORM 時的連結。

如果分析服務中的 createUser 函數,就會發現它的作用隻是對密碼進行哈希處理,而後将 User 對象儲存在資料庫内。以此為基礎,我們編寫出以下測試套件:

沒經過這些測試,你的微服務架構也敢進入生産環境?

首先,我們編寫一個 beforeAll 函數來建立測試子產品。接下來,使用模拟類替換原始 repository,該模拟類将僅傳回需要儲存在資料庫中的對象。

在這個函數中,我們還得考慮這樣一種極端要求:

使用特定屬性(郵件、密碼等)建立一個新使用者對象,請確定密碼經過哈希處理

這裡,我們會模拟 save() 函數,因為它來自 TypeORM、不屬于測試中的對象單元範疇,使用簡單函數将其覆寫以傳回我們傳遞的對象。

到這裡,我們的工作就很簡單了:檢查在發送對象過程中使用的郵件屬性與哈希密碼是否正确。

內建測試屬于另一種軟體測試方法,用于驗證源代碼單元内的組成功能是否正常。

單元測試的目标是保證代碼符合其設計與功能要求,同時能夠按照預期方式運作。內建測試則更進一步,将不同子產品融合在一起,并測試它們是否能夠正确互動。

在本示例中,我們将 UserModule 與 TypeORM 子產品(存在依賴關系)結合起來,檢查新使用者是否被正确儲存在資料庫内。

這一次,我們仍然需要使用之前提到的函數,隻是具體測試流程有所差別:

沒經過這些測試,你的微服務架構也敢進入生産環境?

這一次,beforeAll 函數不再模拟 userRepository,而直接使用原始庫;此外,我們還添加 databaseModule 以建立指向資料庫的連接配接。

與此同時,由于我們現在使用的是真實資料庫,是以必須編寫對應函數調整資料庫以完成測試。

在測試之前與之後,我們需要清空資料庫,保證不存在任何幹擾内容。

另外,我們還需要手動關閉指向資料庫的連接配接,這樣才能保證測試完成後所有處理程式都被正确關閉。

通過單元測試,我們已經檢查了函數能否正常工作。是以,這裡可以直接測試該函數能夠與 TypeORM 的 save() 方法相結合,進而将新使用者對象存儲在資料庫内。

我們編寫了名為 getOneUserFromDb 的輔助函數,它的作用顧名思義——從資料庫内擷取一個使用者。接下來,檢查郵件與 accountConfimed 屬性是否正确(後者在實體類内應預設設定為 false)。

端到端測試

端到端測試是一種軟體測試方法,旨在持續跟蹤應用程式的整個運作流程是否與設計思路一緻。

這類測試的目标,在于確定應用程式能否在真實場景下按預期方式運作。

到目前為止,我們已經測試了使用者密碼是否經過正确的哈希處理,以及密碼及郵件是否被儲存在資料庫内。

現在,我們需要通過請求測試驗證流程。我們的控制器内包含一條驗證管道,通過測試傳入的有效負載檢查對象是否與 CreateUserDto 相比對。

沒經過這些測試,你的微服務架構也敢進入生産環境?

下面來看測試過程:

沒經過這些測試,你的微服務架構也敢進入生産環境?

在這裡,我們希望通過測試觀察系統在建立使用者之後,是否會發送完整對象或者以錯誤格式發送屬性。

這就是我們在某些極端情況下,使用三種基礎軟體測試方法得出的示例結果。

手動測試與自動測試

說到這裡,我們的測試過程一直以手動方式編寫——因為示例規模不大,是以過程非常順利。但如果代碼量龐大,那麼測試的複雜度與工作量會急劇增加。

例如,如果需要測試身份驗證系統,大家就必須複制真實使用者的完整行為。另外,在測試環境的建構階段還需要模拟請求與響應部分,包括 cookie 及其他内容。很明顯,測試套件越複雜,運作需要的時間就越長。

幸運的是,自動化工具已經成為目前測試工作中的有力武器。這類工具包含多種内置功能,允許使用者模拟整個測試環境,輕松搞定手動方式幾乎無法實作的測試流程。

大家還可以走得更遠,在應用程式中用上 API 自動測試工具。這些工具帶有多種附加選項,能夠高效生成負載測試、回歸測試與實際運作狀況等資料報告。

另外,它們還擁有良好的 UI 設計,進一步降低測試編寫難度。

總結

要讓軟體真正為生産環境做好準備,測試絕對是不可或缺的一環。而随着應用程式複雜性的持續提升,測試工作很可能成為開發團隊的最大瓶頸。

在這種情況下,請確定按照具體類型将測試套件區分開來,正如我們在示例中的做法。想要測試哪類功能,就使用與之對應的套件,保證事半功倍。

如果手動套件不足以滿足用例要求,或者您發現測試太難且編寫耗時太長,那麼不妨選擇自動化工具及平台。目前這類方案已經相當成熟,絕對能夠成為各位日常工作中的好幫手。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/zhibo

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-04-09

本文作者:Anton Lawrence

本文來自:“

InfoQ

”,了解相關資訊可以關注“