原文地址:http://www.csdn.net/article/2015-05-26/2824771-continuous-integration-deployment-for-ios-projects
當實現新功能時,如果忽略可維護性而引入技術債務,那將會需要延遲解決它或導致增加維護成本。
最近我們已經思考通過哪些方式來提高程式碼的質量:
- 當程式碼的質量下降時,通過設定一些工具來馬上提醒開發者;
- 文件化一些編碼規範和思考在過去的幾個專案中如何避免維護性差的問題。
我將會簡單地概括我們需要設定什麼才能自動監控程式碼質量。
基礎
我們選擇一個持續整合工具Jenkins,讓它執行在一臺放在我們工作室的Mac Mini。其實我不怎麼喜歡Jenkins,但到目前為止,它是最穩定和最適合的工具來完成這些工作。
我們已經通過Homebrew和rbenv來分別安裝Jenkins和Ruby,而rbenv能夠為我們提供一個最新和穩定的Ruby Gems環境。有個Homebrew和Ruby Gems兩個包管理工具之後,我們就幾乎能夠安裝所有我們需要的工具,但很少會破壞與原有OS X系統更新提供的Ruby。
單元測試
我們使用Specta和Expecta來測試我們的iOS專案。
Specta讓我們採用行為驅動開發(BDD)風格的語法來編寫測試,相比於XCTest的語法,它更加易讀。它還有一個強大的分組測試功能,在測試之前或之後執行一些程式碼塊,這樣的話,能夠極大地減少重複程式碼。
Expecta是一個匹配器框架,我們可以在測試中使用它來建立斷言。它的語法非常強大,與此同時,它比內建的XCAssert套件更加易讀。例如:
expect(@"foo").to.equal(@"foo"); expect(foo).notTo.equal(1); expect([bar isBar]).to.equal(YES); expect(baz).to.equal(3.14159);
我們在開發時,通過XCode來執行測試;而使用通過Homebrew來安裝的Jenkins時,會藉助XCTool。XCTool是一個可代替的選擇來xcodebuild,它能讓你通過命令列的方式來非常輕鬆地執行測試套件和生成JUnit風格的測試報告。
$ xctool -workspace Project.xcworkspace -scheme Project -reporter junit:junit-report.xml test
這些測試報告會發布在Jenkins上,而Jenkins會使用JUnit Plugin來根據時間的推移提供單元測試結果的圖表,同時會向我們顯示我們的測試是否穩定。
Unit Test
Pull Request測試
我們想我們的測試儘可能執行以至於如果我們破壞什麼東西,我們就會馬上知道。我們在feature branches做些修改,然後提交一個pull request到Github,那麼程式碼就會被另一個開發者審查。只要被開啟,我們就能執行所有的測試來確保沒有任何東西被破壞。
當新的pull requst是開放狀態時,為了管理這些,我們安裝Github Pull Request plugin來將資訊從Github傳送到Jenkins。如果有任何測試失敗,它將會顯示在Github,然後我們就不將程式碼合併,直到程式碼被修復為止。
程式碼覆蓋率
我們也會用Gcovr工具來生成程式碼覆蓋率報告,Gcovr的安裝方式也是Homebrew。你需要針對main target的debug congfiguration改變兩個構建設定來配置專案。將Generate Test Coverage Files和Instrument Program Flow都設定為YES。
Code Coverage
當我們執行單元測試來生成程式碼覆蓋率報告時,我們需要將OBJROOT=./build新增到XCTool命令列的尾部。
$ gcovr -r . — object-directory build/Project.build/Debug-iphonesimulator/Project.build/ Objects-normal/x86_64 — exclude ‘.*Tests.*’ — xml > coverage.xml
Gcovr輸出的程式碼覆蓋率報告也會被外掛Cobertura Jenkins plugin釋出,這個外掛會提供一種視覺化的方式來根據時間的推移來顯示程式碼覆蓋率。
現在我們不僅可以看到測試是否通過,還可以看到程式碼的測試覆蓋範圍。
靜態分析
在工具集中,其中一個強大並能夠保持高質量的程式碼的工具就是靜態分析工具。這些工具會掃描你的程式碼,然後生成一個報告,這個報告會告訴你破壞程式碼風格規則的程式碼位置。舉幾個規則的例子:
- 未使用的變數或引數
- 長變數名,方法名或程式碼行
- 覆蓋一個方法,但沒有在這個方法呼叫super
- 方法太長或方法過於複雜
- 還更多的規格...
我們使用OCLint靜態分析工具,這個工具能夠支援C,C++和Objective-C語言。OCLint通過結合XCTool使用來生成json-compilation-database reporter,從而提供great integration特性。我們首先新增另一個reporter到我們的XCTool命令列,然後將那個report傳遞到OCLint來執行靜態分析。
$ xctool -workspace Project.xcworkspace -scheme Project -reporter junit:junit-report.xml -reporter json-compilation-database:compile_commands.json test $ oclint-json-compilation-database -e Pods -report-type pmd -o oclint-pmd.xml
這個report以PMD的方式來生成,然後使用PMD Plugin被髮布到Jenkins。有了這些外掛之後,你也可以在測試失敗之前,設定每個警告的優先順序(底,中,高)中一些限制。最初,我們設定這些限制為低,那麼只要我們引入程式碼,就會被提醒,從而提高程式碼質量。
Static Analysis
自動部署
最後一個問題不是如何提高程式碼質量,而是如何節省時間。開發者通常都會將編譯好的程式碼通過Crashlytics傳送到設計師來設計審查,或在sprint結束演示時發給使用者。傳送一個已經編譯好的App通常花一個開發者的10分鐘左右時間,但它需要他們來切換任務和干擾他們的心流。
最近我們已經配置一個在夜晚構建系統,它會在早上自動傳送一個新版本的App給每個人。
為了做到這樣,我們使用fastlane。fastlane是一個定義lanes的一些操作來執行的強大工具集。現在我們有三個已經定義好的lanes,一個是用來發布給ribot開發者,一個是用來發布給在ribot的每個人,最後一個是釋出給使用者。
before_all do |lane| cert sigh end desc “Deploy a new build to ribot iOS developers over crashlytics” lane :dev do ipa crashlytics({ groups: ‘ribot-developers’ }) end desc “Deploy a new build to people at ribot over crashlytics” lane :internal do ensure_git_status_clean append_build_time ipa crashlytics({ groups: ‘ribot’ }) reset_git_repo end desc “Deploy a new build to everyone over crashlytics” lane :external do ensure_git_status_clean increment_build_number ipa crashlytics({ groups: [‘ribot’, ‘client’] }) commit_version_bump add_git_tag push_to_git_remote end after_all do |lane| clean_build_artifacts end
通過使用fastlane工具(通過Ruby Gems來安裝)來執行一個lane。
fastlane internal
在開始使用所有的lanes之前,我們應該自動確保我們有一個有效的signing certificate和最新的provisioning profile。所有我們的配置都放在一個.env檔案,它讓我們有些預設配置,但當我們執行fastlane根據需要來覆蓋它們。
在將來,我們會通過使用deliver操作來自動化App Store提交過程。
最後總結
到目前為止,我們已經嘗試這些過程,並在工程中呈現出好的結果。我們期望看到只要適當地使用這些工具,就能提高程式碼的質量,這些報告將會讓我們隨著時間推移來量化程式碼質量。我們期待在下一個工程中適當地使用這些工具會發生什麼。