袁宜霞

關鍵詞:敏捷開發模式;持續集成;自動化測試;CI流水線
0 引言
隨著互聯網時代的到來,傳統的瀑布式開發無法應對需求快速變化的缺點越來越明顯。面對市場商機的競爭情況,各大企業需要快速上線產品并獲得市場反饋,所以各大企業普遍采用敏捷開發模式[1]。敏捷開發是以用戶需求為核心,把一個大項目拆分為多個可以獨立運行的小項目[2],在研發過程中,軟件始終處于可使用狀態。在現有產品基礎上,研發團隊通過增加代碼來豐富功能和優化用戶體驗,每個團隊成員每天都在生成和上傳代碼。未經過驗證的代碼合并后,如果不能快速檢查發現問題,會影響各自的功能或關聯模塊,而如果每個開發人員都手動執行來檢查問題,又會影響效率,特性的高頻率交付與軟件質量之間的矛盾日漸尖銳,如何平衡兩者成為研發團隊需要重點解決的問題。持續集成[3]就是解決這一個問題的利器,其采用高頻率自動化檢查代碼方式,減少了手工執行錯誤,避免了重復性勞動,且更快地發現錯誤提高了產品質量,是一種常見的敏捷工程實踐方法。增量代碼只有徹底解決持續集成過程中發現的問題才能通過持續集成的驗證。
本文制定了一套持續集成的流程,介紹了本地IDE、合并請求MR和CI流水線等階段的具體實踐過程,并詳細闡述了實踐過程中的關鍵點。
1 持續集成的流程
為了同時兼顧質量與效率,在提高發布頻率的情況下保證可靠性,持續集成在代碼集成、功能測試、部署發布、基礎設施架構管理等各個環節都應該有全面的自動化監控手段,盡量避免人工介入,圖1是一套持續集成的流程。
1.1 本地集成開發環境IDE 階段
開發團隊都具有版本控制功能的代碼庫,各大公司一般使用SVN或者Git對代碼進行版本管理。開發人員在個人工作區完成代碼開發后,先執行自動化驗證集,用于驗證自己修改的代碼質量是否達標,只有通過了自動化驗證才能push代碼到主干。自動化驗證包括本次增量以及相關代碼的單元測試和代碼規范掃描。
1) 單元測試
單元測試是指對軟件中的最小可測試單元進行檢查和驗證,可以是函數級、方法級、類級或者功能模塊級,本地IDE環境執行的僅僅是本地增量代碼的單元測試。單元測試用例的編寫者既可以是開發人員也可以是測試人員:開發人員編寫用例的優勢在于熟悉自身編寫的開發代碼,加上擁有較強的編程技能,所以單元測試編寫覆蓋率和效率都比較高,劣勢在于開發人員的開發時間緊張,普遍缺乏測試思維且代碼自寫自測無法達到絕對的客觀;測試人員編寫用例的優勢在于具有較強的測試思維可以保證覆蓋率,借由編寫用例的機會能更好地了解代碼架構和實現流程,有利于后續的功能測試,劣勢在于代碼實現能力相對較弱,效率會比較低。在各個業務線中,可以根據自身團隊人員和產品特點來實際情況來確定單元測試用例的編寫者。
不同編程語言使用不同的單元測試框架,比如Java語言普遍使用Junit或TestNG[4],Python語言普遍使用Unittest或者Pytest[5]。評估單元測試的重要標準是代碼覆蓋率,需要盡量覆蓋全部的開發代碼,常見的覆蓋包括語句覆蓋、判定覆蓋、條件覆蓋、判定-條件覆蓋、條件組合覆蓋和路徑覆蓋。
2) 代碼規范掃描
代碼規范掃描是指不需要編譯過程,通過詞法分析、語法分析和抽象語法樹分析等技術手段直接掃描源代碼,以便檢查代碼是否滿足規范性和是否有漏洞。針對不同開發語言,業界有多種不同的代碼規范掃描工具,持續集成可以依據是否開源、適用的編程語言、掃描結果展示、掃描速度、擴展性和可維護性等方面選擇幾種工具進行接入以達到更好的掃描效果。例如Findbugs能掃描Java代碼,PCLint和Cppcheck能掃描C/C++,golangci-lint 能掃描Go 語言,Pylint 和Flake8 能掃描Python,ESLint 能掃描JavaScrip 和NodeJs,Coverity Prevent和騰訊開源的TScanCode能掃描C/C++、C#,Sonarqube 開源且能支持Java、Python、PHP、JavaScript、CSS等25種以上的語言的掃描等[6]。
1.2 合并請求MR 階段
本地驗證通過后,開發人員將本地修改的代碼與主干上已經更新的代碼進行合并,再執行一次合入前檢查,確保本地修改的代碼與主干上最新代碼的合并沒有質量問題。這次質量驗證會執行產品全量代碼的單元測試、集成測試和代碼掃描規范,其中任意一項驗證未通過都會結束合并請求并通知開發人員進行修改。與本地IDE階段相比,單元測試和代碼掃描規范由本地增量和相關代碼變成全量代碼,新增了全量代碼的集成測試。集成測試的主要是通過模擬真實的用戶場景,從最終用戶的體驗出發對多個已完成單元測試的模塊進行模塊間調用和集成,對被測系統的集成性和數據完整性進行測試,其重點關注模塊間的接口和集成后的功能,常見的有針對API調用的接口測試和利用Selenium[7]和Appium[8]等測試工具來實現的UI自動化測試等。當通過合入前檢查后,本地修改代碼才正式合入主干。
1.3 CI 流水線階段
市面上有很多的CI 工具,無論是新興輕量的工具Drone,還是老牌的Jenkins工具都原生或通過插件方式支持了配置文件管理流水線[9]這一特性。這樣一方面不再需要一個Web頁面專門用于流水線管理,減少了維護成本,另一方面,將流水線配置集成在源碼倉庫中,享受與源碼同步升級的方式,使得CI 流程也能使用Git 的版本管理進行規范與審計溯源,如下是關鍵階段。
1) 提交構建
持續集成服務器發現主干代碼變更后,立即開始執行提交構建,運行自動化質量驗證。如果這次構建失敗,則直接阻塞結束本次構建,研發團隊負責人立即著手修復,為了盡快獲得軟件質量反饋,提交構建的執行時間不應超過團隊規定時間,比如15分鐘,所以這個階段自動化驗證的是運行速度較快且質量高的測試用例,主要包括:增量代碼和相關依賴代碼的單元測試用例、安全/漏洞掃描、BVT核心準入測試等,其中安全/漏洞掃描工具主要有Burp Suite、Nessus、AWVS等[10]。
2) 次級構建
這個步驟在流水線里是可選步驟,當自動化測試用例的規模增加到一定程度,無法在團隊規定時間內完成提交構建的所有質量驗證時,可以在提交構建通過后立刻啟用次級構建。通常把運行時間長且不經常失敗的測試用例放到次級構建,在執行次級構建時并不阻塞其他工作任務開展。次級構建會執行全量的單元測試和集成測試,如果次級構建驗證失敗,也要求立即通知研發團隊負責人進行修復,并通知其他開發成員在問題修復前,不能再次提交代碼。放入次級構建的自動化測試用例主要包括執行時間長耗費資源多的或者優先級低出錯可能性低的用例。
3) 編譯打包
通過提交構建和次級構建的基礎質量驗證后,當前主干的代碼就是一個可以直接部署的版本,將這個版本的所有文件進行編譯打包存檔到生產服務器,做好發布準備。
2 持續集成流程的關鍵點
2.1 主干開發,頻繁提交代碼
研發團隊內的開發人員從主干上拉出個人分支,完成開發任務和通過單元測試后,以每天至少一次的頻率將其新增代碼合并到主干中。主干開發可以大大加快產品迭代的效率,但是也會讓新完成的功能特性無法得到全面的手工測試和驗證,所以要實現主干開發、頻繁提交,必須實現如下策略:
1) 研發團隊采用小批量開發模式,把項目拆分成多個小項目后,能夠較短時間內實現小項目的需求。
2) 團隊積累全面且有效的自動化測試用例,包括全量的單元測試和集成測試,擁有極高的測試覆蓋率、準確率和有效率,才能保障主干代碼質量的穩定,持續集成過程不需介入人工驗證也可以讓團隊對代碼的質量比較有信心。
3) 提交代碼到主干前,開發人員進行代碼走查和審核能提高新增代碼的質量,有助于提升新增代碼通過各種自動化驗證的概率,確保代碼能順暢地合并到主干中。
4) 構建和測試過程應該在15分鐘內完成,過長的等待時間會提高將代碼合并到主干的成本,提高了持續集成效率才能提升團隊的研發效率。
5) 高度自動化且精準的線上及灰度監控能力能為采用持續集成的項目進行質量保底。通過持續集成驗證發布出去版本一旦有嚴重缺陷,強大的線上灰度監控能力能讓團隊在短時間內發現異常,停止新版本發布,并緊急修復缺陷后快速進行驗證再次發布。
2.2 團隊分支也需要持續集成
研發團隊有時也會采用分支開發集成發布的研發模式,比如重大底層重構類技術需求或者是數據版本升級,研發團隊會從主干上拉出團隊分支,多人在該分支上頻繁提交和構建代碼,開發人員在本地完成開發任務后,把代碼合入團隊分支。這種團隊分支通常會與主干并行存在較長一段時間,在分支驗證通過后直接在分支上進行灰度發布,灰度發布的數據穩定后把團隊分支代碼合入主干,再跟隨主干進行全量發布。針對這種團隊分支,團隊將其設置為保護分支,僅允許在所有測試通過后才合并拉取請求,并且跟主干一樣所有的代碼提交自動觸發CI流水線,強制執行持續集成以保證團隊分支代碼質量的穩定。
2.3 單元測試由開發人員負責測試人員輔助,集成測試由測試人員負責
單元測試的對象是模塊內部的程序,為了消除局部模塊的功能和邏輯上的缺陷,在持續集成中采用白盒自動化測試方法。由熟悉自己實現的功能代碼的開發人員負責單元測試是事半功倍的,鑒于開發人員對測試知識的不足,測試人員負責單元測試代碼的走查。對于重點功能的單元測試,由開發和測試采用結對編程的方式完成能達到質量和效率的平衡。集成測試的對象是模塊間的集成和調用關系,采用白盒測試和黑盒UI自動化測試方法。這類測試需要對整個程序功能和實現方式比較了解,專業的測試人員負責比較合理。在項目實施過程中,采用自頂向下方式和自底向上方法結合的集成測試用例編寫能達到發現缺陷時機和編寫成本的平衡。
2.4 測試用例分級處理
在實際項目發布時,存在小范圍的灰度發布和針對全部用戶的全量發布兩種形式。一般情況下,集成測試用例的自動化執行是持續集成中最耗時的步驟,采用用例分級方式能進一步提升發布效率。對于集成測試用例,標注1級為程序最重要的用例,灰度發布和全量發布都需要執行;標注2級為次重要用例,灰度發布不需要執行僅在全量發布時執行;標注3級為不穩定用例,需要優化代碼提高穩定性。
3 結束語
綜上,持續集成將軟件交付過程中開發、測試和運維的環節打通,通過自動化測試與監控來提高研發效率和質量,其主要特點如下文。
3.1 構建自動化
持續集成提供讓軟件自動編譯和鏈接到可執行文件的能力,在運行集成構建時,會識別出代碼沖突、編譯錯誤等基本問題,這類低級別高影響的問題需要在代碼合并后快速被發現和處理,所以需要高頻的自動化構建。
3.2 測試自動化
在迭代過程中,研發團隊需要重新測試之前版本中的可行功能,保證產品功能的穩定性。所以,在開發代碼的同時,團隊也需要編寫對應的單元測試和集成測試用例,在構建產品增量代碼時自動觸發這些不斷累積的測試用例的執行,能更早發現功能性和邏輯性缺陷,減少項目提交測試后的代碼缺陷。
3.3 豐富的插件應用
持續集成系統能應用豐富的插件來提高系統的穩定性和安全性,比如代碼靜態掃描和安全掃描等都可以嵌入到持續集成中,一方面保證了功能的質量,另一方面也促使團隊優化系統架構、減少冗余代碼、提升系統安全性,讓交付的產品更加完美。