構建iOS持續集成平臺
 
作者 劉先寧,火龍果軟件    發布于 2013-11-11
 

(一)——自動化構建和依賴管理

2000年Matin Fowler發表文章Continuous Integration【1】;2007年,Paul Duvall, Steve Matyas和 Andrew Glover合著的《Continuous Integration:Improving Software Quality and Reducing Risk》 【2】出版發行,該書獲得了2008年的圖靈大獎。持續集成理念經過10多年的發展,已經成為了業界的標準。在Java, Ruby的世界已經誕生了非常成熟的持續集成工具和實踐,而對于iOS領域來說,因為技術本身相對比較年輕和蘋果與生俱來的封閉思想,在持續集成方面的發展相對滯后一些,但是,隨著越來越多的iOS開發者的涌入,以及各個互聯網巨頭加大對iOS開發的投入,誕生了一大批非常好用的持續集成工具和服務,本文的目的就是介紹一下如何有效的利用這些類庫,服務快速構建一個iOS開發環境下的持續集成平臺。

自動化構建

在MartinFowler的文章[1]中關于自動化的構建定義如下:

Anyone should be able to bring in a virgin machine, check the sources 
out of the repository, issue a single command, and have a running
system on their machine.

因此,自動化構建的的首要前提是有一個支持自動化構建的命令行工具,可以讓開發人員可以通過一個簡單的命令運行當前項目。

命令行工具

自動化構建的命令行工具比持續集成的概念要誕生得早很多,幾十年前,Unix世界就已經有了Make,而Java世界有Ant,Maven,以及當前最流行的Gradle,.Net世界則有Nant和MSBuild。作為以GUI和命令行操作結合的完美性著稱的蘋果公司來說,當然也不會忘記為自己的封閉的iOS系統提供開發環境下命令行編譯工具:xcodebuild【3】

xcodebuild

在介紹xcodebuild之前,需要先弄清楚一些在XCode環境下的一些概念【4】:

Workspace:簡單來說,Workspace就是一個容器,在該容器中可以存放多個你創建的Xcode Project, 以及其他的項目中需要使用到的文件。使用Workspace的好處有,1),擴展項目的可視域,即可以在多個項目之間跳轉,重構,一個項目可以使用另一個項目的輸出。Workspace會負責各個Project之間提供各種相互依賴的關系;2),多個項目之間共享Build目錄。

Project:指一個項目,該項目會負責管理生成一個或者多個軟件產品的全部文件和配置,一個Project可以包含多個Target。

Target:一個Target是指在一個Project中構建的一個產品,它包含了構建該產品的所有文件,以及如何構建該產品的配置。

Scheme:一個定義好構建過程的Target成為一個Scheme。可在Scheme中定義的Target的構建過程有

Build/Run/Test/Profile/Analyze/Archive

BuildSetting:配置產品的Build設置,比方說,使用哪個Architectures?使用哪個版本的SDK?。在Xcode Project中,有Project級別的Build Setting,也有Target級別的Build Setting。Build一個產品時一定是針對某個Target的,因此,XCode中總是優先選擇Target的Build Setting,如果Target沒有配置,則會使用Project的Build Setting。

弄清楚上面的這些概念之后,xcodebuild就很好理解了,官網上對其作用的描述如下:

xcodebuild builds one or more targets contained in an Xcode 
project, or builds a scheme contained in an Xcode workspace or
Xcode project.

xcodebuild就是用了構建產品的命令行工具,其用法可以歸結為3個部分:

1.可構建的對象

2.構建行為

3.一些其他的輔助命令

可以構建的對象有,默認情況下會運行project下的第一個target:

1.workspace:必須和“-scheme”一起使用,構建該workspace下的一個scheme。

2.project:當根目錄下有多個Project的時候,必須使用“-project”指定project,然后會運行

3.target:構建某個Target

4.scheme:和“-workspace”一起使用,指定構建的scheme。

5.……

構建行為包括:

1.clean:清除build目錄下的

2.build: 構建

3.test: 測試某個scheme,必須和"-scheme"一起使用

4.archive:打包,必須和“-scheme”一起使用

5.……

輔助命令包括:

1.-sdk:指定構建使用的SDK

2.-list:列出當前項目下所有的Target和scheme。

3.-version:版本信息

4.…...

關于xcodebuild更多詳細的命令行請參見:https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html

下圖是使用XcodeBuild運行一個scheme的build的結果:

了解了xcodebuild的用法之后,接下來分析一下xcodebuild的主要缺陷:

1.從上圖直接可以得到的感覺,其腳本輸出的可讀性極差,

2.只能要么完整的運行一個target或者scheme,要么全部不運行。不能指定運行Target中特定的測試。

3.最令人發指的是,XCode 4中的xcodebuild居然不支持iOSUnitTest的Target【5】,當我嘗試運行一個iOS App的測試target時,得到如下的錯誤:

對于上面提到的缺陷,Facebook給出了他們的解決方案:xctool【6】

xctool

xctool在 其主頁直接表明了其目的:

xctool is a replacement for Apple's xcodebuild that makes it easier  to build and
test iOS and Mac products. It's especially helpful for continuous integration.

其作用是替代xcodebuild,目的是讓構建和測試更加容易,更好的支持持續集成。從個人感受來看,它的確成功取代了xcodebuild。但是xctool說到底只是對xcodebuild的一個封裝,只是提供了更加豐富的build指令,因此,使用xctool的前提是xcodebuild已經存在,且能正常工作。

安裝

xctool的安裝非常簡單,只需要clone xctool的repository到項目根目錄就可以使用, 如果你的機器上安裝有Homebrew,可以通過“brew install xctool”命令直接安裝。(注意:使用xctool前一定要首先確認xcodebuild已安裝且能正確工作)。

用法

關于xctool的用法就更加人性化了,幾乎可以重用所有的xcodebuild的指令,配置。只需要注意一下幾點:

1.xctool不支持target構建,只能使用scheme構建。

2.支持“-only”指令運行指定的測試。

3.支持多種格式的build報告。

例子:

path/to/xctool.sh 
-workspaceYourWorkspace.xcworkspace
-schemeYourScheme
test -only SomeTestTarget:SomeTestClass/testSomeMethod

下圖是我使用xctool運行test的效果:

常見問題:

No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=armv7 armv7s).

解決方法:到Project Setting中,把"Build Active Architecture Only"設置為NO

Code Sign error: A valid provisioning profile matching the application's Identifier 'dk.muncken.MyApp' could not be found

解決方法:通過“-sdkiphonesimulator”指定SDK,從而能夠使用符合iOS約定的application Identifier。

依賴管理

選定了命令行工具之后, 接下來可以考慮下依賴管理的問題了。我到現在還記得幾年前,剛從Ant轉到使用Maven的那種爽快的感覺。后來,進入Ruby的世界,其與生俱來的Gem管理系統,也讓其依賴管理變得極其簡單。 對于iOS平臺來說,在做項目時,經常需要使用到各種各樣的第三方Framework,這同樣需要一個爽快的依賴管理系統,不然的話,各位可以想象一下重復的下載Framework文件,拖入各個Target的Build Phase的Link Binary With Libraries中的場景。這種重復的勞動對于“懶惰”的程序員來說,是很難接受的,于是,活躍的社區開發者們提供了這樣的一個工具:Cocoapods【7】

Cocoapods開始于2011年8月12日,經過2年多的發展,現在已經超過2500次提交,并且持續保持活躍更新,目前已成為iOS領域最流行的第三方依賴管理工具。從技術層面來說,其是一個Ruby Gem,從功能層面來說,其是一個iOS平臺下的依賴管理工具,為iOS項目開發提供了類似于在Ruby世界使用Gem的依賴管理體驗。

安裝

前面提到cocoapods本質上是一個Ruby Gem,因此,其使用前提首先是Ruby開發環境。慶幸的是,Mac下都自帶Ruby。這樣,只需要簡單的2條命令,就可以把cocoapods安裝好:

$ [sudo] gem install cocoapods
$ pod setup

用法

cocoapods的使用方式和使用Ruby Gem非常相似,首先需要在項目根目錄下創建文件Podfile,在Podfile中,開發人員只需要按照規則配置好如下內容就好:

1.項目支持的平臺,版本(iOS/OSX)

2.每個target的第三方依賴

例子:

platform :ios, '6.0'
inhibit_all_warnings!
xcodeproj `MyProject`
pod 'ObjectiveSugar', '~> 0.5'
target :test do
pod 'OCMock', '~> 2.0.1'
end
post_install do |installer|
installer.project.targets.each do |target|
puts "#{target.name}"
end
end

修改好配置文件之后,只需要簡單的使用“pod install”即可安裝好所有的依賴,執行該命令之后,在項目跟目錄下會出現“.xcworkspace”和“Pods”兩個目錄:

接下來,開發者需要使用xcworkspace打開項目而不是使用xcodeproject,打開項目之后,在項目目錄下除了自己的project以外,還可以看到一個叫做Pods的項目,該項目會為每一個依賴創建一個target:

在Podfile中,還可以指定依賴專屬于某個Target,

target :CocoaPodsTest do
pod 'OCMock', '~> 2.0.1'
pod 'OCHamcrest'
end

如果你記不清楚某個依賴庫的名稱,可以使用“pod search <name>”模糊搜索依賴庫中的相似庫,另外,如果你想使用的庫在cocoapods的中央庫中找不到,那么,你可以考慮為開源社區做做貢獻,把你覺得好用的庫添加到中央庫中,Cocoapods的官網上有具體的步驟【8】

原理

CocoaPods的原理思想基本上來自于Jonah Williams的博客“Using Open Source Static Libraries in Xcode 4”【9】,當使用“pod install”安裝文件時,cocoapods做了如下這些事:

1.創建或者更新當前的workspace

2.創建一個新的項目來存放靜態庫

3.把靜態庫會編譯生成的libpods.a文件配置到target的build phase的link with libraries中

4.在依賴項目中創建*.xcconfig文件,指定在編譯時的一些參數和依賴

5.添加一個新的名為“Copy Pods Resource”的Build Phase,該build phase會使用"${SRCROOT}/Pods/Pods-CocoaPodsTest-resources.sh"把Pods下的資源文件拷貝到app bundle下。

注意事項

當使用xctool作為命令行工具構建項目時,使用cocoapods管理依賴時,需要做一些額外的配置:

1.編輯Scheme,把pods靜態庫項目作為顯式的依賴添加到項目的build中,

2.把pods依賴項目拖動到本來的項目之上,表示先編譯pods靜態庫項目,再編譯自己的項目。

(二)——測試框架

測試框架

有了自動化構建和依賴管理之后,開發者可以很輕松的在命令行構建整個項目,但是,作為持續集成平臺來說,最重要的還是測試,持續集成最大的好處在于能夠盡早發現問題,降低解決問題的成本。而發現問題的手段主要就是測試。在Martin Fowler的Test Pyramid【10】一文中論述了測試金子塔的概念,測試金字塔的概念來自Mike Cohn,在他的書Succeeding With Agile中有詳細描述:測試金字塔最底層是單元測試,然后是業務邏輯測試,如果更細化一點的話,可以分為把完整的測試策略分為如下的層級:

作為持續集成平臺,能自動化的測試層級越多,平臺就能產生越大的價值。

Unit Test

目前,在iOS領域, 最流行的Unit測試框架有2個:OCUnit【11】和GHunit【12】,這兩個框架各有其優缺點:

 優點
缺點
OCUnit與Xcode無縫集成, 快捷鍵,Scheme配置都非常方便1. 只能一次運行整個測試,不能靈活的運行某個測試集; 2.測試結果輸出的可讀性不好,不容易找到失敗的測試
GHUnit1.自帶GUI,測試結果清晰可見;2.可以靈活的運行指定的測試;3.開源項目1.需開發者安裝,配置略顯復雜;2. 對命令行運行測試的支持不是很好,
   

OCUnit的運行結果會通過彈窗直接告訴開發者,運行的細節信息則會打印在Xcode的輸出窗口中:

GHUnit的運行結果則全部顯示在自己的應用界面中,開發者可以在應用中查看所有的信息,以及做運行測試等各種各樣的操作。

關于如何使用OCUnit和GHUnit, InfoQ上有高嘉峻的文章《iOS開發中的單元測試》(http://www.infoq.com/cn/articles/ios-unit-test-1)有詳細的介紹,我就不再這兒重復敘述了。

如果單從單元測試框架來看,個人更喜歡GHUnit測試結果的可讀性和運行測試的靈活性,但是,隨著Facebook的xctool的發布,OCUnit華麗麗的逆襲了,因為xctool幫助OCUnit把運行測試的靈活性和測試結果的可讀性這兩塊短板給補齊了,再加上其和Xcode的集成優勢以及通過命令行運行的便捷性,讓其成為持續集成平臺的Unit測試框架的首選。

在Java程序員的心中,Junit和Hamcrest永遠是一體的,Hamcrest為junit提供了非常豐富的斷言機制,極大的增強了測試的可讀性。越來越活躍的iOS開發社區,當然不會讓Object-C的世界缺失這樣一個優秀的框架,于是OCHamcrest【13】誕生了。

在測試項目中使用OCHamcrest非常簡單,尤其是使用了cocoapods管理依賴的項目。只需要在Podfile文件中加上:

target :<TestTargetName> do
...
pod 'OCHamcrest'
end

然后,運行“pod install”命令安裝Hamcrest到測試Target,安裝好之后,為了在測試類中使用OCHamcrest的斷言。還需要在測試類的頭文件中加入如下代碼:

#define HC_SHORTHAND
#import<OCHamcrest/OCHamcrest.h>

開發者可以把這段代碼加入<TestTargetName>-prefix.pch中,這樣所有的測試類就都可以使用OCHamcrest的斷言了。在前面提到的高嘉峻的文章中的第二部分更加詳細的講解了OCHamcrest的斷言,以及其和另一個斷言框架Expecta的對比,感興趣的同學可以跳過去看看。

Component Test & Integration Test

在開發手機應用時,總難免會和其他的系統集成,尤其當開發的應用是某個系統的手機客戶端時,這樣就涉及到很多第三方API的集成點需要測試,在成熟的Java世界中,誕生了EasyMock,Mockito,moco等針對這種集成點的測試工具。同樣的,活躍的社區力量正一步一步的在讓Object-C世界成熟,OCMock【14】誕生。

OCMock

有了cocoaPods,新加框架變得非常容易,基本上就是“哪里沒有加哪里”的節奏,添加OCMock框架,只需要在Podfile文件中加上:

target :<TestTargetName> do
...
pod 'OCMock', '~> 2.0.1'
end

然后,運行“pod install”命令安裝OCMock到測試Target,同樣的,需要把OCmock的頭文件添加<TestTargetName>-prefix.pch文件中

#import<OCMock/OCMock.h>

下面是一個簡單的使用OCMock的例子,更多的用法請參考OCMock的官網:http://ocmock.org/features/:

- (void)testSimpeMockPass{
idmockObject = [OCMockObjectmockForClass:NSString.class];
[[[mockObject stub] andReturn:@"test"] lowercaseString];

NSString * returnValue = [mockObjectlowercaseString];
assertThat(returnValue, equalTo(@"test"));
}

moco

moco【15】以其對系統集成點測試的貢獻榮獲了2013年的“Duke's Choice Award”獎,雖然其以Java寫成,主要的生態系統也是圍繞Java打造,但是,其Standalone模式可以非常方便的構造一個開發者期望的服務器,這對于Mobile領域的集成點測試來說,本就是一個非常好的Mock服務器的工具。Moco有如下的特點:

易于配置,使用:一個Json配置文件,然后“java -jar moco-runner--standalone.jar -p 8080 ***.json”就可以啟動一個Mock服務器。該服務器的所有行為都在配置文件里。如果你想添加,修改服務器行為,只需要修改一下配置文件,然后重新啟動該服務器就行了。

配置文件可讀性好,使用Json格式的配置文件,對絕大多數開發者來說都可以很容易理解。

支持模擬客戶端需要的所有http操作,moco實現了針對請求Content、URI、Query Parameter、Http Method、Header、Xpath的模擬。對響應的格式支持有Content、Status Code、Header、URL、甚至支持Sequence請求,即根據對同一請求的調用次數返回不同的結果。

完全開源,代碼不多也比較易懂,如果沒有覆蓋到我們的場景,完全可以在該項目基礎上實現一個自己的Mock服務器 。

熊節在infoQ上發表的《企業系統集成點測試策略》【16】一文中,詳細的論述了在企業系統中,moco對測試系統集成點的 幫助。這些論點在Mobile開發領域同樣適用,因此合理的使用moco可以幫助iOS開發者更加容易的構建一個穩固的持續集成平臺。

System Test

對于iOS的系統(UI)測試來說,比較知名的工具有UIAutomation【17】和FonMonkey【18】。

UIAutomation

UIAutomation是隨著iOS SDK 4.0引入,幫助開發者在真實設備和模擬器上執行自動化的UI測試。其本質上是一個Javascript的類庫,通過 界面上的標簽和值的訪問性來獲得UI元素,完成相應的交互操作,從而達到測試的目的,類似于Web世界的Selenium。

通過上面的描述,可以得知,使用UIAutomation做測試時,開發者必須掌握兩件事:

1.如何找到界面上的一個UI元素

2.如何指定針對一個UI元素的操作

在UIAutomation中,界面就是由一堆UI元素構建的層級結構,所有UI元素都繼承對象UIAElement ,該對象提供了每個UI元素必須具備的一些屬性:

1.name

2.value

3.elements

4.parent

5.…

而整個界面的層級結構如下:

Target(設備級別的UI,用于支持晃動,屏幕方向變動等操作)
Application(設備上的應用,比方說Status Bar,keyboard等)
Main window(應用的界面,比方說導航條)
View(界面下的View,比方說UITableView)
Element(View下的一個元素)
Child element(元素下的一個子元素)

下面是一個訪問到Child element的例子:

UIATarget.localTarget().HamcrestDemo().tableViews()[0].cells()[0].elements()

開發者還可以通過“UIATarget.localTarget().logElementTree()”在控制臺打印出該target下所有的的elements。

找到UI元素之后,開發者可以基于該UI元素做期望的操作,UIAutomation作為原生的UI測試框架,基本上支持iOS上的所有UI元素和操作,比方說:

1.點擊按鈕,例: ***.buttons[“add”].tap()

2.輸入文本, 例:***.textfields[0].setValue(“new”)

3.滾動屏幕,例:***.scrollToElementWithPredicate(“name begin with ’test’”)

4.……

關于使用UIAutomation做UI測試,推薦大家一定要看一下2010的WWDC的Session 306:Automating User Interface Testing with Instruments【19】。 另外,這兒還有一篇很好的博客,詳細的講解了如何使用UIAutomation做UI自動化測試:http://blog.manbolo.com/2012/04/08/ios-automated-tests-with-uiautomation

Apple通過Instruments為UIAutomation測試用例的命令行運行提供了支持,這樣就為UIAutomation和CI服務器的集成提供了便利。開發者可以通過如下的步驟在命令行中運行UIAutomation測試腳本

指定目標設備,構建被測應用,該應用會被安裝到指定的DSTROOT目錄下

xcodebuild
-project "/Users/twer/Documents/xcodeworkspace/AudioDemo/AudioDemo.xcodeproj"
-schemeAudioDemo
-sdk iphonesimulator6.1
-configuration Release SYMROOT="/Users/twer/Documents/xcodeworkspace/
AudioDemo/build" DSTROOT="/Users/twer/Documents/xcodeworkspace/AudioDemo/
build" TARGETED_DEVICE_FAMILY="1"
install

啟動Instruments,基于第一步生成的應用運行UIAutomation測試

instruments
-t "/Applications/Xcode.app/Contents/Applications/Instruments.app/
Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/
Automation.tracetemplate" "/Users/twer/Documents/xcodeworkspace/AudioDemo
/build/Applications/TestExample.app"
-e UIASCRIPT <absolute_path_to_the_test_file>

為了更好的展示測試效果以及與CI服務器集成,活躍的社區開發者們還嘗試把UIAutomation和Jasmine集成: https://github.com/shaune/jasmine-ios-acceptance-tests

UIAutomation因其原生支持,并且通過和Instruments的絕佳配合,開發者可以非常方便的使用錄制操作自動生成測試腳本,贏得了很多開發者的支持,但是因蘋果公司的基因,其系統非常封閉,導致開發者難以擴展,于是活躍的社區開發者們開始制造自己的輪子,Fone Monkey就是其中的一個優秀成果。

Fone Monkey

Fone Monkey是由Gorilla Logic 公司創建并維護的一個iOS自動化測試工具,其功能和UIAutomation差不多,但是由于其開源特性,極大的解放了活躍開發者的生產力,開發者可以很容易的根據自身需要擴展其功能。

Fone Monkey的安裝雖然簡單,但是比UIAutomation的原生支持來說,也算是一大劣勢了,具體的安裝過程可以參考:http://www.gorillalogic.com/fonemonkey-ios/fonemonkey-setup-guide/add-fonemonkey-your-xcode-project

Fone Monkey的使用方式主要就是錄制/回放,也可以把錄制好的測試用例保存為腳本以其他方式運行。安裝好Fone Monkey啟動測試以后,應用界面會有點變化:

開發者通過點擊Record按鈕錄制操作,點擊Play按鈕播放之前錄制的操作,點擊More按鈕可以添加一些針對元素的驗證, 這樣就形成了一個測試用例。

在Fone Monkey中錄制的測試用例可以保存為3種格式,以支持多種運行方式:

1.scriptName.fm:用于支持在Fone Monkey的窗口中運行測試

2.scriptName.m:用于和Xcode的OCUnit test 集成,可以以OCUnit的測試用例的形式運行UI測試,這就讓UI具備了命令行運行和與CI集成的能力。

3.scriptName.js:UIAutomation的格式,用于支持在Instruments中,以UIAutomation的形式運行測試。

(三)——CI服務器與自動化部署

CI服務器

寫到這兒,對于iOS開發者來說,需要準備好:

一個比較容易獲取的源代碼倉庫(包含源代碼)
一套自動化構建腳本
一系列圍繞構建的可執行測試
接下來就需要一個CI服務器來根據源代碼的變更觸發構建,監控測試結果。目前,業界比較流行的,支持iOS構建的CI服務器有Travis CI和Jenkins

Travis CI

Travis CI【20】是一個免費的云服務平臺,主要功能就是為開源社區提供免費的CI服務,對于商業用戶可以使用Travis Pro版本,其基本上支持所有目前主流的語言,Object-C自然也在其中。但是,Travis CI只支持github極大的限制了其應用場景。但是也由于其只支持github,其把和github的集成做到了極致的平滑,易用,因此,對于本就把github作為代碼托管平臺的項目來說,Travis CI可以做為第一選擇。

使用Travis CI只需要簡單的2步即可為你托管在github的項目增加一個CI服務器

第一步:在項目的根目錄下創建travis CI配置文件“.travis.yml”,在配置文件指定語言,環境要求,構建腳本等

language: objective-c
before_install:
- brew update
- brew install xctool
script: xctool -project LighterViewControllerDemo.xcodeproj -scheme
LighterViewControllerDemo -sdkiphonesimulator test

第二步:使用github賬號登陸Travis CI,在賬戶的repositories開啟該項目的自動構建功能,該設置會在github上該項目repository中開啟對Travis CI的Service Hook

下圖就是我的一個iOS項目在Travis CI的構建記錄:

除此之外,Travis CI還非常貼心的提供了一個指示燈,可以讓開發者在自己的repository展示構建狀態。使用指示燈的方法很簡單,只需要在repository的README.md中添加下面這行代碼就行了。

[![Build Status](https://travis-ci.org/xianlinbox/iOSCIDemo.png)](https://travis-ci.
org/xianlinbox/iOSCIDemo)

效果如下:

Jenkins

Jenkins【21】經過多年的發展,其活躍的社區和豐富的插件讓其成為了業界最受歡迎的CI服務器。通過使用Xcode插件,可以非常方便在Jenkins中運行iOS項目的構建腳本。

安裝

Jenkins的安裝非常簡單,尤其是在Mac環境下使用Homebrew安裝,只需要簡單的使用“brew install jenkins”,即可成功安裝,這個安裝過程做的事情非常簡單,就是把對應版本的jenkins.war文件下載到對應的目錄“/usr/local/Cellar/jenkins/1.524/libexec/”。因此,如果沒有Homebrew,可以直接到官網下載安裝文件,自己安裝。

配置安裝完之后,只需要使用“java -jar */jenkins.war”即可啟動Jenkins,開發者可以自己創建一個bash alias簡化輸入,在用戶目錄下的.bashrc文件中添加如下代碼

aliasjenkins='java -jar /Applications/Jenkins/jenkins.war'

啟動Jenkins之后,可以通過瀏覽器訪問http://localhost:8080查看Jenkins界面。

然后,開發者可以到Manage Plugins界面,安裝需要的插件:Xcode Plugin,Git Plugin,Github Plugin(我使用git做源代碼管理),Cocoapods Plugin。安裝好插件之后,需要重啟Jenkins,一切就緒之后,開始配置自己的iOS構建過程。

在Jenkins中配置一個iOS Job的步驟如下:

1.新建Job,Job類型選擇“Build a free-style software project”。

2.配置代碼倉庫,

3.點擊“Add build step”添加構建步驟,如果已安裝Xcode插件,則可以在Step類型中看到Xcode選項:

選擇Xcode,可以看到Xcode構建step的所有配置選項:

4.點擊“Add build step”添加測試步驟,選擇“Execute shell”選項,然后,添加腳本,執行測試并生成期望的Report, 可以重復本步驟添加“Acceptaince Test”等構建步驟。

path-to/xctool.sh 
-workspaceAudioDemo.xcworkspace -scheme AudioDemo -sdkiphonesimulator
-reporter junit:test-reports/junit-report.xml clean test

5.點擊“Add post-build action”添加一個新的步驟,選擇“publish Junit test result report”,把測試報告展示在Jenkins中。

配置好之后,可以點擊Build Now運行Job,下圖是在Jenkins中運行iOS構建 Job的結果圖:

開發者可以點擊每次的構建,查看具體的構建信息及測試結果:

常見問題

啟動Jenkins提示運行“java”命令需要X11支持,

解決方法:這是因為在OS X Mountain Lion系統中不再內置X11,當需要使用X11時,可通過安裝XQuartz project得到支持,具體信息:http://support.apple.com/kb/HT5293

自動化部署

這兒的想談的“部署”不是傳統意義上的直接部署到產品環境的部署,而是指如何把最新版本的應用快速的部署到測試用戶的機器上以收集反饋,或者做一些探索性的測試。

在我寫第一個iOS應用的時候,我想把應用安裝到多個機器上測試的時候,需要非常繁瑣的步驟:

1.需要申請到蘋果開發者賬號,獲得開發者證書。

2.需要在蘋果的開發者網站上注冊我想使用的設備。

3.使用開發者證書打包應用,使用Ad-HOC部署模式,生成ipa文件。

4.通過ipa文件把應用安裝到iTunes上。

5.通過iTunes把應用同步到多臺測試機器上。

如果是測試機器在多個地理位置的時候,還需要把ipa文件發送到對應的地點,每個地點都需要重復的做第4,5步。 這樣一個繁瑣,且低效的過程讓開發者非常痛苦,直到TestFlight的出現。

TestFlight

TestFlight【22】就是一個專門解決上面提到的痛點的云服務方案,它可以幫助開發者:

1.輕松采集測試用戶的UDID和iOS 版本、硬件版本,并發送給開發者。

2.實時反饋應用是否成功安裝到測試機器

3.輕松部署最新版本應用到測試用機上。

4.開發者可以靈活選擇部署哪個版本到哪部分測試機器上。

使用使用Test Flight服務非常簡單,只需要到Test Flight注冊一個賬號。然后把鏈接發送給測試設備,測試設備只要打開該鏈接,并授權給Test Flight,在Test Flight的設備中心就可以看到這些設備。

而測試設備上,則會多一個Test Flight的應用。

當應用有了新的測試包之后,只需要將IPA上傳到TestFlight網站,然后勾選合適的測試用戶或者合適的設備,點擊Update & Notify。

TestFlight會向對應的測試設備發送更新通知,測試用戶只需在測試設備上打開TestFlight應用,點擊Install,TestFlight就會自動將新版本的IPA文件安裝到測試設備上。

另外,TestFlight還提供了Upload API,這樣就等于提供了和CI服務器集成的能力。其Upload API非常簡單,就是一個簡單的,帶有指定參數的HTTP POST請求,具體細節可參考官網:https://www.testflightapp.com/api/doc/。

在 CI服務器中,開發者可以在構建過程中,添加步驟通過upload API把通過測試的ipa文件自動上傳到TestFlight上,從而達到持續部署的目的。而像Jenkins這樣有豐富插件機制的CI服務器, 活躍的社區開發者們早為其開發了十分便于使用的TestFlight的插件。

在Jenkins中使用TestFlight插件也非常簡單,安裝好插件,重啟Jenkins,然后在項目的構建過程配置頁面的Post-build Actions中,點擊add post-build action,可以看到Upload to Testflight選項:

選擇之后,可以在頁面上看到TestFlight的配置項:

為了增強安全性,該插件把Token的設置移到了Jenkins的Global Setting界面:

配置好Token Pair之后,在在TestFlight的配置項 上選擇相應的pair即可,設置想要的參數,保存即可。

你如果不喜歡使用插件或者說使用的其它CI服務器的話,可以通過添加一個Execute shell步驟,直接通過代碼上傳構建好的ipa文件:

curl http://testflightapp.com/api/builds.json      
-F file=@testflightapp.ipa
-F dsym=@testflightapp.app.dSYM.zip
-F api_token='your_api_token'
-F team_token='your_team_token'
-F notes='This build was uploaded via the upload API'
-F notify=True
-F distribution_lists='Internal, QA'

結語

《持續集成》一書中引用了Javaranch.com的創始人Kathy Sierra的一句話:

There's a big difference between saying, "Eat an apple a day" and actually eating the apple

正所謂知易行難,您幾乎很難聽到開發者說:“持續集成毫無價值”,但是,構建一個持續集成平臺并非易事,希望本文中介紹的iOS應用的構建過程,以及在構建過程的各個階段可以使用的一些優秀的類庫,服務,能夠讓iOS開發者們在想搭建一個持續集成平臺時有所參考,從而能夠更加堅定,且容易的為自己的項目搭建一個持續集成平臺。