iOS UITests(UI自動化測試 實現(xiàn))

iOS UITests(UI自動化測試 實現(xiàn))

本文Demo地址
[toc]

簡介

1.UI Tests是一個自動測試UI與交互的Testing組件,它可以通過編寫代碼葵蒂、或者是記錄開發(fā)者的操作過程并代碼化,來實現(xiàn)自動點擊某個按鈕、視圖罚斗,或者自動輸入文字等功能山孔。
2.在實際的開發(fā)過程中狞贱,隨著項目越做越大缚够,功能越來越多,僅僅靠人工操作的方式來覆蓋所有測試用例是非常困難的题造,尤其是加入新功能以后傍菇,舊的功能也要重新測試一遍,這導(dǎo)致了測試需要花非常多的時間來進行回歸測試界赔,這里產(chǎn)生了大量重復(fù)的工作丢习,而這些重復(fù)的工作有些是可以自動完成的,這時候UI Tests就可以幫助解決這個問題了.


home.gif

最低要求

Xcode7
iOS 9

使用

1.添加UI Teste Targets
新工程 創(chuàng)建工程的時候勾選 Inclide UI Tests

Paste_Image.png

現(xiàn)有的工程 新建一個 Targets 選擇 iOS UI Testing Bundle

Paste_Image.png
Paste_Image.png

添加完成 添加完成可以看到UITests如圖:

Paste_Image.png

自動化測試代碼的編寫

方法1.自己看著界面編寫

參考文章下面的代碼演示
iOS UITests(自動化測試)常用類參考文檔
參考Demo

方法2.錄制后修改

1.點擊UITests類 下面的 紅色的錄制按鈕

Paste_Image.png

然后APP會啟動淮悼,你只需要對APP進行界面操作咐低,系統(tǒng)就會在你光標最后停留的位置開始生成與你操作對應(yīng)的代碼,然后你稍作修改就可以用了袜腥。
看大大大圖
recode.gif

自動生成的代碼
Paste_Image.png

稍作修改见擦,把里面的Unicode編碼 轉(zhuǎn)成中文,如下
Paste_Image.png

這樣就可以直接運行了

運行自動化測試

1.command + U 運行UITests類中 每一個以test開頭的方法 如:'- (void)allTestExample' '- (void)testHome' 個人感覺是沒有順序的

Paste_Image.png

2.點擊每一個以test開頭的方法前面的 播放 按鈕(運行單個方法)
Paste_Image.png

涉及到的新類

iOS UITests(自動化測試)常用類參考文檔

XCUIApplication - 啟動程序


1.簡介

這個類掌管應(yīng)用程序的生命周期羹令,代表被測應(yīng)用繼承

XCUIElement - 元素


1.簡介

繼承NSObject鲤屡,實現(xiàn)協(xié)議XCUIElementAttributes, XCUIElementTypeQueryProvider
可以表示系統(tǒng)的各種UI元素
XCUIElementQuery - 查找元素


用于定位用戶界面元素的查詢

iOS UITests(自動化測試)常用類參考文檔


代碼演示

https://github.com/ywdonga/iOSUITestsDemo
為了更好的演示 我用storyboard搭建了一個可以簡單跳轉(zhuǎn)的界面

Jietu20170317-093727.png

1.我們從首頁的測試開始(未登錄的首頁)

測試首頁我們首先得判斷是否已經(jīng)登錄,因為已登錄和未登錄是無法進入 我的福侈、資訊 這兩個界面的酒来。在選擇這兩個頁面的時候會直接彈出登錄界面,測試流程就會有差異肪凛。所以就得先判斷當(dāng)前狀態(tài)是否是已登錄狀態(tài)堰汉。

1.1 判斷登錄狀態(tài)
因為我們不能訪問原有APP的任何變量辽社,所以只能通過某個界面特有的元素Element 來進行判斷。在這里我們看到 如果未登錄的話 點擊‘我的’ 一定會跳轉(zhuǎn)到‘登錄’界面翘鸭,而在登錄界面有一個‘登錄’Label. 在這里 我就通過 登錄界面中的一個 文字為‘登錄’的Label元素 作為判斷對象(當(dāng)然你可以根據(jù)自己的想法去定滴铅,但是一定要確保元素的唯一性)。
流程如下

Paste_Image.png

/** 判斷是否在登陸界面 */
- (BOOL)isLogin{
    BOOL isLogin;
    //進入我的界面
    XCUIElementQuery *tabBarsQuery = self.app.tabBars;
    XCUIElement *tableBarButton = tabBarsQuery.buttons[@"我的"];
    !tableBarButton.exists?:[tableBarButton tap];
    //獲取標志性Element loginLabel
    XCUIElement *loginLabel = self.app.staticTexts[@"loginLabel"];
    //判斷是否登陸
    if(loginLabel.exists){//進入登陸界面(當(dāng)前狀態(tài)未登陸)
        XCUIElement *cancerButton = self.app.buttons[@"取消"];
        !cancerButton.exists?:[cancerButton tap];
        isLogin = NO;
    }else{//進入我的頁面(當(dāng)前狀態(tài)已經(jīng)登陸)
        tableBarButton = tabBarsQuery.buttons[@"首頁"];
        !tableBarButton.exists?:[tableBarButton tap];
        isLogin = YES;
    }
    return isLogin;
}

上面代碼中為何定義了一個變量(BOOL isLogin)去接收登錄狀態(tài)就乓,為何不直接用 ‘登錄’元素是否存在(loginLabel.exists)作為結(jié)果直接返回呢汉匙?
原因在于,在判斷登錄時档址,我們進入到其他界面了盹兢,為了不影響后面的測試流程邻梆,我們在判斷完后最好要回到'首頁'守伸。然而在我們回到首頁時候,登錄元素在首頁是找到不到的浦妄,'loginLabel.exists'直接為NO了尼摹。

1.2 退出登錄
判斷完登錄狀態(tài)后,就會出現(xiàn)兩種情況了剂娄,登錄/未登錄蠢涝,由于我們現(xiàn)在的目的是測試未登錄的首頁,那么如果是已經(jīng)登錄了就先退出登陸

logOut.gif

流程就是
判斷是否登陸 ->已經(jīng)登陸 去‘我的’界面找到‘退出登陸’按鈕阅懦,點擊退出登陸

- (void)testLogOut{
    //判斷是否登陸
    if([self isLogin]){
        NSLog(@"--> 開始退出登陸");
        //進入我的頁面
        XCUIElement *tableBarButton = self.app.tabBars.buttons[@"我的"];
        !tableBarButton.exists?:[tableBarButton tap];

        //向上滑一點 避免小屏幕看不到按鈕
        XCUIElement *tableView = [self.app.tables elementAtIndex:0];
        [tableView swipeUp];
        
        XCUIElementQuery *tablesQuery = self.app.tables;
        [tablesQuery.buttons[@"退出登陸"] tap];
        
        //彈窗點擊
        [self alertViewTap];

        //判斷是否登陸成功
        XCTAssert(![self isLogin], @"退出登陸失敗和二,快查查原因");
        
        NSLog(@"---> 成功退出登陸");
    }else{
        NSLog(@"---> 本來就沒有登陸");
    }
}

1.3 測試未登陸首頁
未登錄首頁測試,目前我寫的Demo只是對首頁的每個元素進行查找和簡單的操作
如圖

home.gif

- (void)testHome{
    //判斷是否登陸
    if([self isLogin]){
        [self testLogOut];//退出登陸
    }
    //UISegmentedControl (可見文字)
    XCUIElement *segmentedButton = self.app.segmentedControls.buttons[@"Second"];
    !segmentedButton.exists?:[segmentedButton tap];
    
    segmentedButton = self.app.segmentedControls.buttons[@"First"];
    !segmentedButton.exists?:[segmentedButton tap];
    
    //UITabBarController
    XCUIElementQuery *tabBarsQuery = self.app.tabBars;
    XCUIElement *tableBarButton = tabBarsQuery.buttons[@"我的"];
    !tableBarButton.exists?:[tableBarButton tap];
    
    tableBarButton = self.app.buttons[@"取消"];
    !tableBarButton.exists?:[tableBarButton tap];
    
    //UISlider (通過標簽)
    XCUIElement *slider1 = self.app.sliders[@"slider1"];
    !slider1.exists?:[slider1 adjustToNormalizedSliderPosition:0];
    !slider1.exists?:[slider1 adjustToNormalizedSliderPosition:1];

    //UISwitch (通過標簽)
    XCUIElement *switch1 = self.app.switches[@"switch1"];
    !switch1.exists?:[switch1 tap];
    !switch1.exists?:[switch1 tap];
    
    //UIStepper (通過順序)
    XCUIElement *stepper = [self.app.steppers elementAtIndex:0];
    XCUIElement *incrementButton = stepper.buttons[@"Increment"];
    [incrementButton tap];
    [incrementButton tap];
    [incrementButton tap];
    
    XCUIElement *decrementButton = stepper.buttons[@"Decrement"];
    [decrementButton tap];
    [decrementButton tap];
    [decrementButton tap];
    
    //tableview (通過順序)
    XCUIElement *tableview = [self.app.tables elementAtIndex:0];
    [tableview swipeDown];
    [tableview swipeUp];
    [tableview swipeDown];
    
    //cells
    XCUIElementQuery *cellQuery = tableview.cells;
    
    for (int i=0; i<5; i++) {
        //cell (通過順序)
        [self cellTapAtIndex:i withCells:cellQuery];
        //UIAlertView
        [self alertViewTap];
    }
}

1.4 登錄測試(多賬號)
這里我們準備了多個賬號耳胎,只有最后一個是對的惯吕,然后依次登錄,如果全部登錄失敗就會進入斷言
XCTAssert((loginIndex<self.accountArr.count), @"所有賬號都無法登陸");
準備一個賬號數(shù)組 填入所有賬號

- (NSArray *)accountArr{
    if(!_accountArr.count){
        _accountArr = @[
                       @{@"name":@"zhangsan", @"pwd":@"123456"},//賬號1
                       @{@"name":@"lisis", @"pwd":@"123456"},//賬號2
                       @{@"name":@"wangmazi", @"pwd":@"123456"},//賬號3
                       @{@"name":@"iss", @"pwd":@"123456"},//賬號4
                       ];
    }
    return _accountArr;
}

效果圖


login.gif

代碼

- (void)testLogin{
    if([self isLogin]){
        NSLog(@"--> 測試登陸 已經(jīng)是登陸狀態(tài)");
        return;
    }
    NSLog(@"---> 開始測試登陸");
    //UITabBarController
    XCUIElementQuery *tabBarsQuery = self.app.tabBars;
    XCUIElement *tableBarButton = tabBarsQuery.buttons[@"我的"];
    !tableBarButton.exists?:[tableBarButton tap];
    
    XCTAssert((loginIndex<self.accountArr.count), @"所有賬號都無法登陸");
    
    //獲取用戶名和密碼
    NSDictionary *accountDict = self.accountArr[loginIndex];
    NSString *userName = accountDict[@"name"];
    NSString *pwd = accountDict[@"pwd"];
    
    //UITextField
    XCUIElement *nameTextField = self.app.textFields[@"用戶名"];
    !nameTextField.exists?:[nameTextField tap];
    //輸入用戶名之前先清除之前的文本
    XCUIElement *clearButton = self.app.buttons[@"Clear text"];
    !clearButton.exists?:[clearButton tap];
    //輸入用戶名
    [nameTextField typeText:userName];
    
    //隱藏鍵盤
    [self hindKeyBoard];
    
    
    XCUIElement *pwdTextField = self.app.secureTextFields[@"密碼"];
    !pwdTextField.exists?:[pwdTextField tap];
    //輸入密碼
    !pwdTextField.exists?:[pwdTextField typeText:pwd];
    
    //隱藏鍵盤
    [self hindKeyBoard];
    
    //開始登陸
    XCUIElement *loginButton = self.app.buttons[@"登錄"];
    !loginButton.exists?:[loginButton tap];
    
    sleep(1);//等待登陸
    
    //根據(jù)標識查看是否登陸成功
    XCUIElement *loginLabel = self.app.staticTexts[@"loginLabel"];
    if(loginLabel.exists){//登陸失敗
        //清除提示框
        [self alertViewTap];
        NSLog(@"---> 賬號%@ 密碼%@ 登陸失敗", userName, pwd);
        //換個賬號 重新登陸
        loginIndex ++;
        [self testLogin];
    }else{//登陸成功
        
        NSLog(@"---> 賬號%@ 密碼%@ 登陸成功", userName, pwd);
        
        XCUIElement *tableBarButton = tabBarsQuery.buttons[@"我的"];
        !tableBarButton.exists?:[tableBarButton tap];
        
        tableBarButton = tabBarsQuery.buttons[@"首頁"];
        !tableBarButton.exists?:[tableBarButton tap];
        
        NSLog(@"---> 登陸測試完成");
    }
}

1.5 資訊頁面測試
測試之前必須是已登錄狀態(tài)怕午,才可以順利進入'資訊'頁面废登,所以得先判斷是否登錄,如果沒有登錄的話 必須先登錄

info.gif
- (void)testInformation{
    NSInteger loginCount = 0;
    if([self isLogin]){//已經(jīng)登陸
        NSLog(@"---> 開始測試資訊頁面");
        XCUIElement *information = self.app.tabBars.buttons[@"資訊"];
        !information.exists?:[information tap];
        //點擊
        XCUIElement *button = self.app.buttons[@"標簽2"];
        [button tap];
        button = self.app.buttons[@"標簽3"];
        [button tap];
        button = self.app.buttons[@"標簽4"];
        [button tap];
        button = self.app.buttons[@"標簽5"];
        [button tap];
        button = self.app.buttons[@"標簽6"];
        [button tap];
        button = self.app.buttons[@"標簽1"];
        [button tap];
        //滑動
        XCUIElement *swipeView = [self.app.scrollViews elementAtIndex:0];
        [swipeView swipeRight];
        [swipeView swipeLeft];
        [swipeView swipeLeft];
        [swipeView swipeLeft];
        [swipeView swipeLeft];
        [swipeView swipeLeft];
        [swipeView swipeLeft];

        NSLog(@"---> 資訊頁面測試完成");
    }else{//未登陸
        XCTAssert((loginCount<MaxLoginNumber), @"");
        NSLog(@"---> 在資訊頁面發(fā)現(xiàn)未登錄 開始第 %@ 次登陸", @(loginCount));
        loginCount ++;
        //發(fā)起登陸
        [self testLogin];
        //繼續(xù)測試資訊
        [self testInformation];
    }
}

參考

iOS UITests(自動化測試)常用類參考文檔
iOS 官方文檔
本文Demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末郁惜,一起剝皮案震驚了整個濱河市堡距,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兆蕉,老刑警劉巖羽戒,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異虎韵,居然都是意外死亡半醉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門劝术,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缩多,“玉大人呆奕,你說我怎么就攤上這事〕倪海” “怎么了梁钾?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逊抡。 經(jīng)常有香客問我姆泻,道長,這世上最難降的妖魔是什么冒嫡? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任拇勃,我火速辦了婚禮,結(jié)果婚禮上孝凌,老公的妹妹穿的比我還像新娘方咆。我一直安慰自己,他們只是感情好蟀架,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布瓣赂。 她就那樣靜靜地躺著,像睡著了一般片拍。 火紅的嫁衣襯著肌膚如雪煌集。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天捌省,我揣著相機與錄音苫纤,去河邊找鬼。 笑死纲缓,一個胖子當(dāng)著我的面吹牛卷拘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播色徘,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼恭金,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了褂策?” 一聲冷哼從身側(cè)響起横腿,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤徒役,失蹤者是張志新(化名)和其女友劉穎彬祖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奕坟,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡遍搞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年罗侯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溪猿。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡钩杰,死狀恐怖纫塌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讲弄,我是刑警寧澤措左,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站避除,受9級特大地震影響怎披,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瓶摆,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一凉逛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧群井,春花似錦状飞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽指蚁。三九已至菩佑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凝化,已是汗流浹背稍坯。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搓劫,地道東北人瞧哟。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像枪向,于是被迫代替她去往敵國和親勤揩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,846評論 25 707
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程秘蛔,因...
    小菜c閱讀 6,373評論 0 17
  • #重逢# 不停的反復(fù) 情緒堆積 一次次推翻 一遍遍重來 唱腔微苦 二哥說 別這么喪 于是云淡風(fēng)輕 呼喚著你 可是 ...
    修柯閱讀 266評論 0 0
  • 挑了閱讀人數(shù)比較高的陨亡,原來qq的崛起是如此的,每一步并沒有我們想象的那么簡單深员,每天每月每年世界都在變換负蠕,我們要做的...
    金魚茵茵閱讀 225評論 0 0
  • 昨天,Sir穿著背心褲衩在激情碼字倦畅。 表妹突然幽幽走過來說—— 你的著裝會真實反映你的靈魂遮糖,嘖嘖嘖…… 身為一個著...
    Sir電影閱讀 3,100評論 7 93