iOS UITests(UI自動(dòng)化測試 實(shí)現(xiàn))

iOS UITests(UI自動(dòng)化測試 實(shí)現(xiàn))

本文Demo地址
[toc]

簡介

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


home.gif

最低要求

Xcode7
iOS 9

使用

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

Paste_Image.png

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

Paste_Image.png
Paste_Image.png

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

Paste_Image.png

自動(dòng)化測試代碼的編寫

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

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

方法2.錄制后修改

1.點(diǎn)擊UITests類 下面的 紅色的錄制按鈕

Paste_Image.png

然后APP會(huì)啟動(dòng),你只需要對APP進(jìn)行界面操作,系統(tǒng)就會(huì)在你光標(biāo)最后停留的位置開始生成與你操作對應(yīng)的代碼,然后你稍作修改就可以用了。
看大大大圖
recode.gif

自動(dòng)生成的代碼
Paste_Image.png

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

這樣就可以直接運(yùn)行了

運(yùn)行自動(dòng)化測試

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

Paste_Image.png

2.點(diǎn)擊每一個(gè)以test開頭的方法前面的 播放 按鈕(運(yùn)行單個(gè)方法)
Paste_Image.png

涉及到的新類

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

XCUIApplication - 啟動(dòng)程序


1.簡介

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

XCUIElement - 元素


1.簡介

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


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

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


代碼演示

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

Jietu20170317-093727.png

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

測試首頁我們首先得判斷是否已經(jīng)登錄,因?yàn)橐训卿浐臀吹卿浭菬o法進(jìn)入 我的、資訊 這兩個(gè)界面的。在選擇這兩個(gè)頁面的時(shí)候會(huì)直接彈出登錄界面,測試流程就會(huì)有差異。所以就得先判斷當(dāng)前狀態(tài)是否是已登錄狀態(tài)。

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

Paste_Image.png

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

上面代碼中為何定義了一個(gè)變量(BOOL isLogin)去接收登錄狀態(tài),為何不直接用 ‘登錄’元素是否存在(loginLabel.exists)作為結(jié)果直接返回呢?
原因在于,在判斷登錄時(shí),我們進(jìn)入到其他界面了,為了不影響后面的測試流程,我們在判斷完后最好要回到'首頁'。然而在我們回到首頁時(shí)候,登錄元素在首頁是找到不到的,'loginLabel.exists'直接為NO了。

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

logOut.gif

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

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

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

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

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

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 (通過標(biāo)簽)
    XCUIElement *slider1 = self.app.sliders[@"slider1"];
    !slider1.exists?:[slider1 adjustToNormalizedSliderPosition:0];
    !slider1.exists?:[slider1 adjustToNormalizedSliderPosition:1];

    //UISwitch (通過標(biāo)簽)
    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 登錄測試(多賬號)
這里我們準(zhǔn)備了多個(gè)賬號,只有最后一個(gè)是對的,然后依次登錄,如果全部登錄失敗就會(huì)進(jìn)入斷言
XCTAssert((loginIndex<self.accountArr.count), @"所有賬號都無法登陸");
準(zhǔn)備一個(gè)賬號數(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ù)標(biāo)識查看是否登陸成功
    XCUIElement *loginLabel = self.app.staticTexts[@"loginLabel"];
    if(loginLabel.exists){//登陸失敗
        //清除提示框
        [self alertViewTap];
        NSLog(@"---> 賬號%@ 密碼%@ 登陸失敗", userName, pwd);
        //換個(gè)賬號 重新登陸
        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),才可以順利進(jìn)入'資訊'頁面,所以得先判斷是否登錄,如果沒有登錄的話 必須先登錄

info.gif
- (void)testInformation{
    NSInteger loginCount = 0;
    if([self isLogin]){//已經(jīng)登陸
        NSLog(@"---> 開始測試資訊頁面");
        XCUIElement *information = self.app.tabBars.buttons[@"資訊"];
        !information.exists?:[information tap];
        //點(diǎn)擊
        XCUIElement *button = self.app.buttons[@"標(biāo)簽2"];
        [button tap];
        button = self.app.buttons[@"標(biāo)簽3"];
        [button tap];
        button = self.app.buttons[@"標(biāo)簽4"];
        [button tap];
        button = self.app.buttons[@"標(biāo)簽5"];
        [button tap];
        button = self.app.buttons[@"標(biāo)簽6"];
        [button tap];
        button = self.app.buttons[@"標(biāo)簽1"];
        [button tap];
        //滑動(dòng)
        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(自動(dòng)化測試)常用類參考文檔
iOS 官方文檔
本文Demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容