Xcode7 UI自動(dòng)化測(cè)試詳解 帶demo UITests

UI Tests是什么?

UI Tests是一個(gè)自動(dòng)測(cè)試UI與交互的Testing組件

UI Tests有什么用稍算?

它可以通過編寫代碼、或者是記錄開發(fā)者的操作過程并代碼化,來實(shí)現(xiàn)自動(dòng)點(diǎn)擊某個(gè)按鈕域仇、視圖,或者自動(dòng)輸入文字等功能寺擂。

UI Tests的重要性

在實(shí)際的開發(fā)過程中暇务,隨著項(xiàng)目越做越大,功能越來越多沽讹,僅僅靠人工操作的方式來覆蓋所有測(cè)試用例是非常困難的般卑,尤其是加入新功能以后,舊的功能也要重新測(cè)試一遍爽雄,這導(dǎo)致了測(cè)試需要花非常多的時(shí)間來進(jìn)行回歸測(cè)試蝠检,這里產(chǎn)生了大量重復(fù)的工作,而這些重復(fù)的工作有些是可以自動(dòng)完成的挚瘟,這時(shí)候UI Tests就可以幫助解決這個(gè)問題了

使用方法

第一步:添加UI Tests

如果是新項(xiàng)目叹谁,則創(chuàng)建工程的時(shí)候可以直接勾選選項(xiàng),如下圖


創(chuàng)建工程
創(chuàng)建工程

如果是已有的項(xiàng)目乘盖,可以通過添加target的方式添加一個(gè)UI Tests焰檩,點(diǎn)擊xcode的菜單,找到target欄


添加target
添加target

在Test選項(xiàng)中選擇Cocoa Touch UI Testing Bundle


添加target_2
添加target_2

這時(shí)候test組件添加成功订框,它在項(xiàng)目中的位置如下圖所示


目錄結(jié)構(gòu)
目錄結(jié)構(gòu)
第二步:創(chuàng)建測(cè)試代碼

手動(dòng)創(chuàng)建測(cè)試代碼
打開測(cè)試文件析苫,在testExample()方法中添加測(cè)試代碼

這里寫圖片描述
這里寫圖片描述

如果不知道如何寫測(cè)試代碼,則可以參考自動(dòng)生成的代碼樣式

自動(dòng)生成測(cè)試步驟
選擇測(cè)試文件后,點(diǎn)擊錄制按鈕

這里寫圖片描述
這里寫圖片描述

這時(shí)候開始進(jìn)行操作衩侥,它會(huì)記錄你的操作步驟国旷,并生成測(cè)試代碼
下圖就是在一些操作后自動(dòng)生成的測(cè)試代碼


這里寫圖片描述
這里寫圖片描述

這時(shí)候可以分析測(cè)試代碼的語法,以便你自己手動(dòng)修改或者手寫測(cè)試代碼

開始測(cè)試
點(diǎn)擊testExample方法旁邊的播放按鈕茫死,它就開始進(jìn)行自動(dòng)測(cè)試了跪但,這時(shí)候你會(huì)看到app在自動(dòng)操作

這里寫圖片描述
這里寫圖片描述

下面介紹一下測(cè)試元素的語法

XCUIApplication:
繼承XCUIElement,這個(gè)類掌管應(yīng)用程序的生命周期峦萎,里面包含兩個(gè)主要方法
launch():
啟動(dòng)程序
terminate():
終止程序

**XCUIElement: **
繼承NSObject屡久,實(shí)現(xiàn)協(xié)議XCUIElementAttributes, XCUIElementTypeQueryProvider
可以表示系統(tǒng)的各種UI元素
exist:
可以讓你判斷當(dāng)前的UI元素是否存在,如果對(duì)一個(gè)不存在的元素進(jìn)行操作爱榔,會(huì)導(dǎo)致測(cè)試組件拋出異常并中斷測(cè)試
descendantsMatchingType(type:XCUIElementType)->XCUIElementQuery:
取某種類型的元素以及它的子類集合
childrenMatchingType(type:XCUIElementType)->XCUIElementQuery:
取某種類型的元素集合被环,不包含它的子類

這兩個(gè)方法的區(qū)別在于,你僅使用系統(tǒng)的UIButton時(shí)搓蚪,用childrenMatchingType就可以了蛤售,如果你還希望查詢自己定義的子Button,就要用descendantsMatchingType

另外UI元素還有一些交互方法
tap():
點(diǎn)擊
doubleTap():
雙擊
pressForDuration(duration: NSTimeInterval):
長按一段時(shí)間妒潭,在你需要進(jìn)行延時(shí)操作時(shí)悴能,這個(gè)就派上用場了
swipeUp():
這個(gè)響應(yīng)不了pan手勢(shì),暫時(shí)沒發(fā)現(xiàn)能用在什么地方雳灾,也可能是beta版的bug漠酿,先不解釋
typeText(text: String):
用于textField和textView輸入文本時(shí)使用,使用前要確保文本框獲得輸入焦點(diǎn)谎亩,可以使用tap()函數(shù)使其獲得焦點(diǎn)

XCUIElementAttributes協(xié)議
里面包含了UIAccessibility中的部分屬性
如下圖

這里寫圖片描述
這里寫圖片描述

可以方便你查看當(dāng)前元素的特征炒嘲,其中identifier屬性可用于直接讀取元素,不過該屬性在UITextField中有bug匈庭,暫時(shí)不清楚原因

XCUIElementTypeQueryProvider協(xié)議
里面包含了系統(tǒng)中大部分UI控件的類型夫凸,可通過讀屬性的方式取得某種類型的UI集合
部分屬性截圖如下

這里寫圖片描述
這里寫圖片描述

創(chuàng)建Demo

首先創(chuàng)建一個(gè)登錄頁面


這里寫圖片描述
這里寫圖片描述

點(diǎn)擊login按鈕進(jìn)行登錄驗(yàn)證,點(diǎn)擊clear按鈕會(huì)清除文本
登錄成功后可以去到個(gè)人信息頁面

個(gè)人信息頁面如下


這里寫圖片描述
這里寫圖片描述

點(diǎn)擊modify按鈕可以修改個(gè)人信息阱持,點(diǎn)擊Message按鈕可以查看個(gè)人消息

最后是消息界面


這里寫圖片描述
這里寫圖片描述
登錄頁面的測(cè)試
  1. 輸入一個(gè)錯(cuò)誤的賬號(hào)
  2. 驗(yàn)證結(jié)果
  3. 關(guān)閉警告窗
  4. 清除輸入記錄
  5. 輸入一個(gè)正確的賬號(hào)
  6. 驗(yàn)證結(jié)果
  7. 進(jìn)入個(gè)人信息頁面
    測(cè)試代碼如下:
    func testLoginView() {
        let app = XCUIApplication()
       
        // 由于UITextField的id有問題夭拌,所以只能通過label的方式遍歷元素來讀取
        let nameField = self.getFieldWithLbl("nameField")
        if self.canOperateElement(nameField) {
            nameField!.tap()
            nameField!.typeText("xiaoming")
        }
       
        let psdField = self.getFieldWithLbl("psdField")
        if self.canOperateElement(psdField) {
            psdField!.tap()
            psdField!.typeText("1234321")
        }
       
        // 通過UIButton的預(yù)設(shè)id來讀取對(duì)應(yīng)的按鈕
        let loginBtn = app.buttons["Login"]
        if self.canOperateElement(loginBtn) {
            loginBtn.tap()
        }
       
        // 開始一段延時(shí),由于真實(shí)的登錄是聯(lián)網(wǎng)請(qǐng)求衷咽,所以不能直接獲得結(jié)果鸽扁,demo通過延時(shí)的方式來模擬聯(lián)網(wǎng)請(qǐng)求
        let window = app.windows.elementAtIndex(0)
        if self.canOperateElement(window) {
            // 延時(shí)3秒, 3秒后如果登錄成功,則自動(dòng)進(jìn)入信息頁面镶骗,如果登錄失敗桶现,則彈出警告窗
            window.pressForDuration(3)
        }
       
        // alert的id和labe都用不了,估計(jì)還是bug鼎姊,所以只能通過數(shù)量判斷
        if app.alerts.count > 0 {
            // 登錄失敗
            app.alerts.collectionViews.buttons["確定"].tap()
           
            let clear = app.buttons["Clear"]
            if self.canOperateElement(clear) {
                clear.tap()
               
                if self.canOperateElement(nameField) {
                    nameField!.tap()
                    nameField!.typeText("sun")
                }
               
                if self.canOperateElement(psdField) {
                    psdField!.tap()
                    psdField!.typeText("111111")
                }
               
                if self.canOperateElement(loginBtn) {
                    loginBtn.tap()
                }
                if self.canOperateElement(window) {
                    // 延時(shí)3秒, 3秒后如果登錄成功骡和,則自動(dòng)進(jìn)入信息頁面相赁,如果登錄失敗,則彈出警告窗
                    window.pressForDuration(3)
                }
                self.loginSuccess()
            }
        } else {
            // 登錄成功
            self.loginSuccess()
        }
    }

這里有幾個(gè)需要特別注意的點(diǎn):

  1. 當(dāng)你的元素不存在時(shí)即横,它仍然可能返回一個(gè)元素對(duì)象噪生,但這時(shí)候不能對(duì)其進(jìn)行操作
  2. 當(dāng)你要點(diǎn)擊的元素被鍵盤或者UIAlertView遮擋時(shí)裆赵,執(zhí)行tap方法會(huì)拋異常
    詳細(xì)實(shí)現(xiàn)可參照demo:
    https://github.com/sunljz/demo/tree/master/iOS9/UITestDemo
個(gè)人信息頁測(cè)試
  1. 修改性別
  2. 修改年齡
  3. 修改心情
  4. 保存修改
    測(cè)試代碼如下:
    func testInfo() {
        let app = XCUIApplication()
        let window = app.windows.elementAtIndex(0)
        if self.canOperateElement(window) {
            // 延時(shí)2秒, 加載數(shù)據(jù)需要時(shí)間
            window.pressForDuration(2)
        }
       
        let modifyBtn = app.buttons["modify"];
        modifyBtn.tap()
       
        let sexSwitch = app.switches["sex"]
        sexSwitch.tap()
       
        let incrementButton = app.buttons["Increment"]
        incrementButton.tap()
        incrementButton.tap()
        incrementButton.tap()
        app.buttons["Decrement"].tap()
       
        let textView = app.textViews["feeling"]
        textView.tap()
        app.keys["Delete"].tap()
        app.keys["Delete"].tap()
        textView.typeText(" abc ")
       
        // 點(diǎn)擊空白區(qū)域
        let clearBtn = app.buttons["clearBtn"]
        clearBtn.tap()

        // 保存數(shù)據(jù)
        modifyBtn.tap()
        window.pressForDuration(2)
       
        let messageBtn = app.buttons["message"]
        messageBtn.tap();
       
        // 延時(shí)1秒, push view需要時(shí)間
        window.pressForDuration(1)
       
        self.testMessage()
    }

這里需要特別注意以下兩點(diǎn):

  1. textview獲取焦點(diǎn)時(shí)無法選擇焦點(diǎn)的位置
  2. tap事件的觸發(fā)位置是view的中心东囚,所以當(dāng)view的中心被遮擋時(shí),要考慮使用其他view來代替
個(gè)人消息界面測(cè)試
  1. 單元格的點(diǎn)擊
    測(cè)試代碼如下:
    func testMessage() {
        let app = XCUIApplication()
        let window = app.windows.elementAtIndex(0)
        if self.canOperateElement(window) {
            // 延時(shí)2秒, 加載數(shù)據(jù)需要時(shí)間
            window.pressForDuration(2)
        }
       
        let table = app.tables
        table.childrenMatchingType(.Cell).elementAtIndex(8).tap()
        table.childrenMatchingType(.Cell).elementAtIndex(1).tap()
       
    }

這里需要注意一點(diǎn):

  1. 暫時(shí)無法獲取到tableView的元素指針

總結(jié)

總的來說战授,UI Tests只能用于一些基礎(chǔ)功能的測(cè)試页藻,驗(yàn)證app的功能是否可以正常使用,是否存在崩潰問題植兰。但它也有很多不足之處份帐,編寫測(cè)試用例的過程非常繁瑣,自動(dòng)生成的代碼幾乎無法運(yùn)行楣导,功能單一废境,很多用例無法覆蓋,而且bug很多筒繁,大大地限制了UI Tests在實(shí)際開發(fā)中的應(yīng)用噩凹。希望正式版出來的時(shí)候能夠修復(fù)這些問題,并開放更多的功能毡咏。

demo地址:
https://github.com/sunljz/demo/tree/master/iOS9/UITestDemo

歡迎各位提出改進(jìn)建議驮宴,感謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呕缭,一起剝皮案震驚了整個(gè)濱河市堵泽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恢总,老刑警劉巖迎罗,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異片仿,居然都是意外死亡纹安,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門滋戳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钻蔑,“玉大人,你說我怎么就攤上這事奸鸯∵湫Γ” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵娄涩,是天一觀的道長窗怒。 經(jīng)常有香客問我映跟,道長,這世上最難降的妖魔是什么扬虚? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任努隙,我火速辦了婚禮,結(jié)果婚禮上辜昵,老公的妹妹穿的比我還像新娘荸镊。我一直安慰自己,他們只是感情好堪置,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布躬存。 她就那樣靜靜地躺著,像睡著了一般舀锨。 火紅的嫁衣襯著肌膚如雪岭洲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天坎匿,我揣著相機(jī)與錄音盾剩,去河邊找鬼。 笑死替蔬,一個(gè)胖子當(dāng)著我的面吹牛告私,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播进栽,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼德挣,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了快毛?” 一聲冷哼從身側(cè)響起格嗅,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唠帝,沒想到半個(gè)月后屯掖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡襟衰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年贴铜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瀑晒。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绍坝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苔悦,到底是詐尸還是另有隱情轩褐,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布玖详,位于F島的核電站把介,受9級(jí)特大地震影響勤讽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拗踢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一脚牍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巢墅,春花似錦诸狭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至庵芭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雀监,已是汗流浹背双吆。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留会前,地道東北人好乐。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像瓦宜,于是被迫代替她去往敵國和親蔚万。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容