工業(yè)時代流水線的發(fā)明將生產(chǎn)任務(wù)的效率大大提升棠赛。同樣高职,在軟件開發(fā)過程中流水線的建立也能幫助我們更好的產(chǎn)出或粮、提升效率器虾。
流水線的建立準則應(yīng)該符合每個團隊自己的需求躬窜,比如你的團隊管理策略浇垦、分支管理策略,接下來以我們最近的給客戶做的案例進行總結(jié)說明:
我們首先需要制定我們流水線的策略荣挨,需要哪幾個任務(wù)男韧,進行任務(wù)的拆分:
iOS:
| 主分支每次提交進行構(gòu)建觸發(fā)單元測試
|
|--功能測試構(gòu)建分發(fā)
| |
| |--各個執(zhí)行功能測試的節(jié)點并行執(zhí)行功能測試
| |
| |--聚合各個節(jié)點的功能測試結(jié)果輸入報告
|
|--adhoc分發(fā)通知測試人員
|
|--enterprise分發(fā)通知測試人員
|
|--appstore提審核
|
Android:
| 主分支每次提交進行構(gòu)建觸發(fā)單元測試
|
|--功能測試構(gòu)建分發(fā)
| |
| |--各個執(zhí)行功能測試的節(jié)點并行執(zhí)行功能測試
| |
| |--聚合各個節(jié)點的功能測試結(jié)果輸入報告
|
|--打release包通知測試人員測試
|
|--打各種渠道包
其中的功能測試板塊由客戶的測試團隊負責(zé)日常的監(jiān)控以及維護朴摊,不干擾開發(fā)團隊日常的開發(fā)。不是整個任務(wù)成功的必要條件此虑,而在我之前的開發(fā)項目中甚纲,功能測試是由開發(fā)人員與測試人員一起結(jié)對書寫,因此會把它作為發(fā)測試包的前置步驟朦前,這點根據(jù)每個團隊的實際情況考量介杆。
工具集:
工欲善其事,必先利其器韭寸。要想自動化整個過程春哨,離不開工具的支持。
通用:
Jenkins:用的最廣持續(xù)集成工具棒仍,但是本身并不提供流水線功能悲靴,需要插件支持
Gem:ruby包管理工具,比如我們執(zhí)行功能測試Appium莫其、Calabash等都是通過gem來安裝的癞尚。
rbenv:管理ruby,用它來統(tǒng)一ruby環(huán)境乱陡。
bundle:用來管理gem包浇揩,比如gem包的版本等。
rake:用ruby時間的類似于make的構(gòu)建工具憨颠,我們的任務(wù)腳本使用rake來寫的胳徽,選自己順手的就好了。
cucumber:基于BDD的自動化測試框架
iOS:
shenzhen:對爽彤,深圳养盗,作者寫了一堆用城市命名的工具。這個是用來構(gòu)建ipa以及分發(fā)的适篙,不過我只用了它的分發(fā)功能往核,還是直接用的xcodebuild構(gòu)建。
calabash:iOS端用來進行自動化功能測試的工具嚷节,基于cucumber聂儒。
Android:
Appium:類似于calabash的自動化測試框架。之所以沒有在安卓上用calabash硫痰,是因為項目中用了螞蟻金融的一個SDK衩婚,其對測試不太友好,不支持像calabash這樣使用Instrumentation的框架效斑。
關(guān)鍵設(shè)計:
持續(xù)對主分支進行構(gòu)建:
我們需要保證開發(fā)團隊的每一次代碼提交都是能工作非春,能通過測試的,相比傳統(tǒng)開發(fā)過程中在最后關(guān)頭進行測試,大大降低了風(fēng)險奇昙。
如下圖坐搔,需要在任務(wù)配置中寫上執(zhí)行策略,比如你想每兩分鐘去檢測一次你的代碼庫有沒有代碼變化敬矩,如果有變化,Jenkins會立刻開始執(zhí)行構(gòu)建蠢挡。
拉取下了代碼之后之后需要構(gòu)建并執(zhí)行單元測試弧岳,iOS使用xcodebuild
,安卓使用gradle
业踏。
如何串連流水線:
當(dāng)構(gòu)建沒問題之后禽炬,我們需要在構(gòu)建后步驟中將下游的任務(wù)串連起來,這里有兩種方式勤家,一種是自動觸發(fā)下游任務(wù)腹尖,一種是手動觸發(fā)。比如我們的發(fā)包步驟就是運營人員手工操作伐脖,執(zhí)行功能測試到合并報告就是自動進行的热幔。
自動觸發(fā):
這里需要選擇Trigger parameterized build on other projects,指明下游任務(wù)的名字讼庇。根據(jù)需要制定觸發(fā)條件绎巨,以及傳遞的參數(shù)等。
手動觸發(fā):
這里需要選擇Build other projects(manual steps)蠕啄,我們需要指定下游任務(wù)的名稱场勤。傳遞git commit過去是為了保證下游任務(wù)產(chǎn)品代碼與當(dāng)前保值一致,除此之外還會傳遞一些預(yù)定義的參數(shù)到下游歼跟。
并行執(zhí)行功能測試
需求是需要在多臺節(jié)點上并行地執(zhí)行功能測試和媳,比如我要在天津的一臺機器用三星 note跑測試,我要在成都的一臺機器用 另外一臺手機跑哈街。
為了解決這個需求留瞳,我需要加入一個多配置的任務(wù),然后在Configuration Matrix中進行配置叹卷,如圖撼港,把能夠執(zhí)行這個任務(wù)的多個節(jié)點給選上。
如何合并多個cucumber報告
這里分兩步骤竹,首先需要把每個節(jié)點的測試結(jié)果收集起來帝牡,然后傳遞到下游去,通過傳遞歸檔文件就可以完成這一步蒙揣。比如我任務(wù)完成了之后會生成一個build
目錄靶溜,我需要把多臺節(jié)點的這個目錄傳遞過去。這里需要注意的點在于,每個節(jié)點生成的結(jié)果會加上自己的機器前綴罩息。比如會像這樣mac_chengdu/build嗤详,我們需要使用通配符 ****/build/**表示。
接下來是合并瓷炮,因為cucumber生成的報告結(jié)果可以是json的葱色,這一步就是在解析json的結(jié)果,我fork了cucumber-html-reporter對進行修改娘香,做成了一個node的命令行工具苍狰,可以參考這里。
關(guān)于自動化測試
iOS和安卓的開發(fā)中本身就提供單元測試的支持烘绽,比如iOS提供XCTest淋昭,安卓有JUnit,根據(jù)需要進行調(diào)整安接,比如iOS上我使用的Kiwi翔忽。
單元測試比較簡單,主要看團隊對這東西的認識盏檐。主要聊一下功能測試這塊遇到的坑歇式。
先說iOS,iOS上采用的calabash糯笙,一個是項目組之前也在用贬丛,二個是我在調(diào)研了appium之后,發(fā)現(xiàn)appium最新版本才開始支持XCUITest做功能測試给涕,存在一些bug且功能不夠完善豺憔,因此果斷上calabash。
在安卓遇到的坑相對來說多一點够庙,首先calabash在安卓上底層是用的Instrumentation
恭应,調(diào)研過calabash的源碼發(fā)現(xiàn)如果要讓它支持UIAutomator
的話,基本上等于重新造一個輪子了耘眨,因此如果你的產(chǎn)品不支持Instrumentation
的話需要注意了昼榛。
那么appium呢,appium 配合cucumber在安卓上看起來不錯的剔难。我開開心心地用了起來胆屿,結(jié)果看報告發(fā)現(xiàn)這家伙在失敗的時候不會主動截圖,看樣子得自己去實現(xiàn)了偶宫,好在cucumber提供了一些hook的方法非迹,比如可以在每個執(zhí)行步驟之后做點什么。
我一開始是在cucumber的AfterStep
中加入了截圖的方法纯趋,可是發(fā)現(xiàn)并沒有什么用憎兽,后來查了一會兒發(fā)現(xiàn)已經(jīng)有人給cucumber提過這個[issue](bundle exec cucumber #{feature}--tags ~@pending --format json --out #{BUILD_DIR}/functionals.json --format NewHtml --out #{BUILD_DIR}/functionals.html)冷离,原來cucumber設(shè)計上就是這樣考慮的,失敗了的步驟不允許hook纯命。
既然cucumber這一層做不了西剥,那只能在appium這一層做手腳了。經(jīng)過觀察亿汞,失敗場景大多是找不到元素瞭空,因此我需要解決的主要問題是在找不到元素的時候進行截圖。我在appium-lib中找到了driver.rb
疗我,看了下它提供了幾個查找元素的方法匙铡,底層是用的selenium-webdriver
進行操作,那么我的需求應(yīng)該在driver這層就能夠解決碍粥,我只需要在這幾個方法執(zhí)行失敗后加上截圖保存的方法就好了。Ruby我不熟悉黑毅,我查了下有幾種方式可以解決嚼摩,你可以新建一個子類重寫這幾個方法,你可以利用ruby的動態(tài)性把這幾個方法給動態(tài)的替換了矿瘦。我這里采用的是第二種方式枕面,代碼如下:
module DriverExtension
def store_callback_after_fail(&block)
@execute = block
end
def take_screenshot_when_fail
@@screenshot_count ||= 0
$filename = "./build/" + "Screenshot-" + "#{@@screenshot_count}" + ".png"
File.delete($filename) if File.exist?($filename)
screenshot($filename)
@execute.call($filename)
end
def find_element(*args)
begin
result = super
result
rescue Exception => e
take_screenshot_when_fail
raise
end
end
...
...
end
module Appium
class Driver
prepend DriverExtension
end
end
采用流水線之后可以大幅度的減少人力成本,比如我們的客戶之前還有安排同事專門負責(zé)構(gòu)建打包上傳等任務(wù)缚去,代碼出了問題也很難追溯到底在哪一步出了錯〕泵兀現(xiàn)在整個流程自動化了后,開發(fā)人員只需要更加專注于手中的開發(fā)任務(wù)易结,測試人員想什么時候測就什么時候測枕荞,要做的只需要點個按鈕就好,是不是很贊搞动!