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就可以幫助解決這個問題了.
最低要求
Xcode7
iOS 9
使用
1.添加UI Teste Targets
新工程 創(chuàng)建工程的時候勾選 Inclide UI Tests
現(xiàn)有的工程 新建一個 Targets 選擇 iOS UI Testing Bundle
添加完成 添加完成可以看到UITests如圖:
自動化測試代碼的編寫
方法1.自己看著界面編寫
參考文章下面的代碼演示
iOS UITests(自動化測試)常用類參考文檔
參考Demo
方法2.錄制后修改
1.點擊UITests
類 下面的 紅色的錄制按鈕
然后APP會啟動淮悼,你只需要對APP進行界面操作咐低,系統(tǒng)就會在你光標最后停留的位置開始生成與你操作對應(yīng)的代碼,然后你稍作修改就可以用了袜腥。
看大大大圖
自動生成的代碼
稍作修改见擦,把里面的Unicode編碼 轉(zhuǎn)成中文,如下
這樣就可以直接運行了
運行自動化測試
1.command + U
運行UITests類中 每一個以test
開頭的方法 如:'- (void)allTestExample' '- (void)testHome' 個人感覺是沒有順序的
2.點擊每一個以
test
開頭的方法前面的 播放 按鈕(運行單個方法)涉及到的新類
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)的界面
1.我們從首頁的測試開始(未登錄的首頁)
測試首頁我們首先得判斷是否已經(jīng)登錄,因為已登錄和未登錄是無法進入 我的福侈、資訊 這兩個界面的酒来。在選擇這兩個頁面的時候會直接彈出登錄界面,測試流程就會有差異肪凛。所以就得先判斷當(dāng)前狀態(tài)是否是已登錄狀態(tài)堰汉。
1.1 判斷登錄狀態(tài)
因為我們不能訪問原有APP的任何變量辽社,所以只能通過某個界面特有的元素Element 來進行判斷。在這里我們看到 如果未登錄的話 點擊‘我的’ 一定會跳轉(zhuǎn)到‘登錄’界面翘鸭,而在登錄界面有一個‘登錄’Label. 在這里 我就通過 登錄界面中的一個 文字為‘登錄’的Label元素 作為判斷對象(當(dāng)然你可以根據(jù)自己的想法去定滴铅,但是一定要確保元素的唯一性)。
流程如下
/** 判斷是否在登陸界面 */
- (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)登錄了就先退出登陸
流程就是
判斷是否登陸 ->已經(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只是對首頁的每個元素進行查找和簡單的操作
如圖
- (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;
}
效果圖
代碼
- (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)怕午,才可以順利進入'資訊'頁面废登,所以得先判斷是否登錄,如果沒有登錄的話 必須先登錄
- (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];
}
}