今天想跟大家分享一下最近我們在單元測試和集成測試上的一些心得。
我們是屬於不到兩個披薩就能吃飽的團隊,嚴格來說,一個披薩就夠了,主要因為我們有怕胖的女孩子和不吃高熱量的老年人。雖然我們人丁並不興旺,但是我們負責着七個項目,有兩個是春節前確定的,我后文中稱它倆為A和B。到目前為止,A和B已經運行了兩個sprint。
B是內部服務,它並不直接服務於用戶(至少目前是這樣的),而是服務於公司內的其他項目,比如A項目。A是直接服務於用戶的,但是A的穩定性一定程度上依賴於B的穩定性。所以如果我們要保證A和B的質量,那么就要做好B的單元測試,A的單元測試和A的集成測試。這里就有個問題,我們人丁單薄,在保證覆蓋率的前提下,如何能夠少做一些操作呢?於是我們自己摸索了一條路,把A的單元測試和集成測試寫到一起!
具體的思路是這樣,我們啟動單元測試的命令是 npm run test (我們的開發語言是typescript),然后通過環境變量、參數或其他啟動命令在同一個啟動入口啟動集成測試,比如我們用 npm run integration (等價於TEST_MODE=integration npm run test)來啟動集成測試。當運行集成測試時,程序自動加載test/specs/ 目錄下以.spec.ts結尾的文件,當運行單元測試時,程序自動加載test/specs/ 目錄下以.spec.ts同時不以.integration.spec.ts結尾的文件。
緊接着要解決第二個問題,寫單元測試時,遇到其他模塊或外部服務,我們通常把它們mock掉。但是在集成測試時,就需要真刀真槍的與外部服務交互。所以在上圖的 environment 文件夾中我們提供了一些類和方法,構建測試時所需的一切環境,比如使用docker啟動db、redis和B服務。(有的團隊喜歡用docker-compose,我覺得是因場景而異,就不展開了)
這樣以后,無論是單元測試還是集成測試,每一條case該怎么寫就怎么寫,程序理應和真實環境中的運行效果一樣。
然后,我們就要開始解決第三個問題,如何能夠保證我們在本地獲取到B服務的鏡像(涉及到權限和版本控制)?為此我們和運維同學溝通后,為指定IP地址開放了一個私有鏡像倉庫,在B服務的CD流水線完成制品job時,把B服務的鏡像向我們的私有倉庫推一份,然后我們通過個人賬號,可以從這個倉庫中自動下載B服務的鏡像。這時A項目集成測試所需的一切條件都就緒了,這是我們實際的運行效果:
由於春節后剛運行了兩個sprint,cases數還不夠多,但是我們的覆蓋率已經保持在93%以上。B項目的單元測試覆蓋率也是93%以上:
集成測試運行結束后,不要忘了清除不需要的container和volume。
這個方案實施起來還是挺順利的,雖然有些小問題,但是基本上1-2天框架就搭建起來了。剩下的就是持續補充和持續維護測試cases。
當然單元測試和集成測試是基礎,還需要通過適當的流程讓它們發揮更大的價值。我們的代碼倉庫使用的是github,基於github的pull request,每次有同學想提交代碼到主分支,只能通過PR的方式。提交PR時會自動觸發我們的CI,運行代碼質量分析和單元測試(有集成測試時優先運行集成測試)。要想PR通過,至少有一個人在code review后點了approve按鈕。而我們內部有要求,無論是feature和bug,必須要有相關的單元測試或者集成測試,同時也有對覆蓋率的要求。通過環環相扣,最終把我們的A服務和B服務的質量保護起來。
將近一個月的遠程辦公,我們並沒有在溝通、協作上出現什么問題,團隊之間還是十分信任。一部分歸功於我們寫程序的人相對來說比較簡單,大多數時間都是封閉在自己的思想里。還有一部分歸功於我們團隊一直對DevOps的工程化實踐非常重視,比如項目初期一定要把集成測試搞起來,讓CI/CD成為基石。不過最重要的一部分,當然是我們一直用我們自己的產品Worktile。一個出身於協作,致力於研發更簡單的團隊,這點事算事嗎?
Worktile 官網:worktile.com
本文作者:孫敬雲
文章首發於「Worktile官方博客」,轉載請注明出處。