精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

iOS自動化測試的那些干貨

移動開發 iOS 自動化
自動化測試就是寫一些測試代碼,用代碼代替人工去完成模塊和業務的測試。其實不管是開發還是測試,如果你在不斷的做重復性工作的時候,就應該問自己一個問題:是不是有更高效的辦法?

前言

如果有測試大佬發現內容不對,歡迎指正,我會及時修改。

大多數的iOS App(沒有持續集成)迭代流程是這樣的   

iOS App(沒有持續集成)迭代流程 

也就是說,測試是發布之前的最后一道關卡。如果bug不能在測試中發現,那么bug

就會抵達用戶,所以測試的完整性和可靠性十分重要。

目前,大多數App還停留在人工測試階段,人工測試投入的成本最低,能夠保證核心功能的使用,而且測試人員不需要會寫代碼。

但是,在很多測試場景下,人工測試的效率太低,容易出錯。舉兩個常見的例子:

  • 一個App的核心功能,在每一次發布版本前的測試必定會跑一遍所有的測試用例,不管對應的業務在當前版本有沒有變化(天知道開發在做業務A的時候,對業務B有沒有影響),如果這次測出新的bug,測試人員在下一次發版測試中,又不得不做這些重復的工作。
  • 開發在寫API請求相關代碼的時候沒有做數據容錯,測試在人工測試的時候都是正常的數據,所以測試通過。上線了之后,后臺配置數據的時候出了點小問題,導致大面積崩潰,boom~。

然后,老板就要過來找你了  

 

[[187977]] 

本文所講解的均是基于XCode 8.2.1,有些概念可能不適用于低版本的XCode

自動化測試

自動化測試就是寫一些測試代碼,用代碼代替人工去完成模塊和業務的測試。

其實不管是開發還是測試,如果你在不斷的做重復性工作的時候,就應該問自己一個問題:是不是有更高效的辦法?

自動化測試有很多優點:

  • 測試速度快,避免重復性的工作
  • 避免regression,讓開發更有信心去修改和重構代碼(個人認為最大的優點)
  • 具有一致性。
  • 有了自動化測試,持續集成(CI)會變得更可靠。
  • 迫使開發人員寫出更高質量的代碼。(自動化測試不通過,代碼不允許合并)

當然,自動化測試也有一些缺點。

  • 開發和維護成本高。
  • 不能完全替代人工測試。
  • 無法完全保證測試的準確性 – 讓代碼去判斷一段邏輯是否正確很容易,但是,讓代碼判斷一個控件顯示是否正確卻沒那么容易。

所以,在做自動化測試之前,首先要問自己幾個問題?

  • 這個測試業務的變動是否頻繁?
  • 這個測試業務是否屬于核心功能?
  • 編寫測試代碼的成本有多少?
  • 自動化測試能保證測試結果的準確么?

通常,我們會選擇那些業務穩定,需要頻繁測試的部分來編寫自動化測試腳本,其余的采用人工測試,人工測試仍然是iOS App開發中不可缺少的一部分。

測試種類

從是否接觸源代碼的角度來分類:測試分為黑盒和白盒(灰盒就是黑盒白盒結合,這里不做討論)。

白盒測試的時候,測試人員是可以直接接觸待測試App的源代碼的。白盒測試更多的是單元測試,測試人員針對各個單元進行各種可能的輸入分析,然后測試其輸出。白盒測試的測試代碼通常由iOS開發編寫。

黑盒測試。黑盒測試的時候,測試人員不需要接觸源代碼。是從App層面對其行為以及UI的正確性進行驗證,黑盒測試由iOS測試完成。

從業務的層次上來說,測試金字塔如圖:   

[[187978]] 

而iOS測試通常只有以下兩個層次:

  • Unit,單元測試,保證每一個類能夠正常工作
  • UI,UI測試,也叫做集成測試,從業務層的角度保證各個業務可以正常工作。

框架選擇

啰里八嗦講的這么多,自動化測試的效率怎么樣,關鍵還是在測試框架上。那么,如何選擇測試框架呢?框架可以分為兩大類:XCode內置的和三方庫。

選擇框架的時候有幾個方面要考慮

  • 測試代碼編寫的成本
  • 是否可調式
  • 框架的穩定性
  • 測試報告(截圖,代碼覆蓋率,…)
  • WebView的支持(很多App都用到了H5)
  • 自定義控件的測試
  • 是否需要源代碼
  • 能否需要連著電腦
  • 是否支持CI(持續集成)
  • ….

我們首先來看看XCode內置的框架:XCTest。XCTest又可以分為兩部分:Unit Test 和 UI Test,分別對應單元測試和UI測試。有一些三方的測試庫也是基于XCTest框架的,這個在后文會講到。由于是Apple官方提供的,所以這個框架會不斷完善。

成熟的三方框架通常提供了很多封裝好的有好的接口,筆者綜合對比了一些,推薦以下框架:

單元測試:

以下三個框架都是BDD(Behavior-driven development) – 行為驅動開發。行為驅動開發簡單來說就是先定義行為,然后定義測試用例,接著再編寫代碼。 實踐中發現,通常沒有那么多時間來先定義行為,不過BDD中的domain-specific language (DSL)能夠很好的描述用例的行為。

  • Kiwi 老牌測試框架
  • specta 另一個BDD優秀框架
  • Quick 三個項目中Star最多,支持OC和Swift,優先推薦。

UI測試

  • KIF 基于XCTest的測試框架,調用私有API來控制UI,測試用例用Objective C或Swift編寫。
  • appium 基于Client – Server的測試框架。App相當于一個Server,測試代碼相當于Client,通過發送JSON來操作APP,測試語言可以是任意的,支持android和iOS。

篇幅有限,本文會先介紹XCtest,接著三方的Unit框架會以Quick為例,UI Test框架側重分析KIF,appium僅僅做原理講解。

XCTest

對于XCTest來說,最后生成的是一個bundle。bundle是不能直接執行的,必須依賴于一個宿主進程。關于XCTest進行單元測試的基礎(XCode的使用,異步測試,性能測試,代碼覆蓋率等),我在這篇文章里講解過,這里不再詳細講解。

  • iOS 單元測試之XCTest詳解

blog.csdn.net/hello_hwc/article/details/46671053

單元測試用例

比如,我有以下一個函數:

  1. //驗證一段Text是否有效。(不能以空字符開頭,不能為空) 
  2.  
  3. - (BOOL)validText:(NSString *)text error:(NSError *__autoreleasing *)error{ 
  4.  
  5.  

那么,我該如何為這個函數編寫單元測試的代碼?通常,需要考慮以下用例:

  1. 輸入以空白字符或者換行符開頭的,error不為空,返回 NO
  2. 輸入正確的內容,error為空,返回YES
  3. 輸入為nil,error不為空,返回 NO (邊界條件)
  4. 輸入為非NSString類型,驗證不通過,返回NO (錯誤輸入)
  5. 特殊輸入字符(標點符號,非英文等等)

UI測試

UI測試是模擬用戶操作,進而從業務處層面測試。關于XCTest的UI測試,建議看看WWDC 2015的這個視頻:

  • UI Testing in Xcode

https://developer.apple.com/videos/play/wwdc2015/406/

關于UI測試,有幾個核心類需要掌握

  • XCUIApplication 測試應用的代理
  • XCUIElement 一個UI上可見的視圖對象
  • XCUIElementQuery 查找XCUIElement

UI測試還有一個核心功能是UI Recording。選中一個UI測試用例,然后點擊圖中的小紅點既可以開始UI Recoding。你會發現:

隨著點擊模擬器,自動合成了測試代碼。(通常自動合成代碼后,還需要手動的去調整)  

 

在寫UI測試用例的時候要注意:測試行為而不是測試代碼。比如,我們測試這樣一個case

進入Todo首頁,點擊add,進入添加頁面,輸入文字,點擊save。

測試效果如下:  

 

 

對應測試代碼:

  1. - (void)testAddNewItems{ 
  2.  
  3.     //獲取app代理 
  4.  
  5.     XCUIApplication *app = [[XCUIApplication alloc] init]; 
  6.  
  7.     //找到第一個tabeview,就是我們想要的tableview 
  8.  
  9.     XCUIElement * table = [app.tables elementBoundByIndex:0]; 
  10.  
  11.     //記錄下來添加之前的數量 
  12.  
  13.     NSInteger oldCount = table.cells.count
  14.  
  15.     //點擊Add 
  16.  
  17.     [app.navigationBars[@"ToDo"].buttons[@"Add"] tap]; 
  18.  
  19.     //找到Textfield 
  20.  
  21.     XCUIElement *inputWhatYouWantTodoTextField = app.textFields[@"Input what you want todo"]; 
  22.  
  23.     //點擊Textfield 
  24.  
  25.     [inputWhatYouWantTodoTextField tap]; 
  26.  
  27.     //輸入字符 
  28.  
  29.     [inputWhatYouWantTodoTextField typeText:@"somethingtodo"]; 
  30.  
  31.     //點擊保存 
  32.  
  33.     [app.navigationBars[@"Add"].buttons[@"Save"] tap]; 
  34.  
  35.     //獲取當前的數量 
  36.  
  37.     NSInteger newCount = table.cells.count
  38.  
  39.     //如果cells的數量加一,則認為測試成功 
  40.  
  41.     XCTAssert(newCount == oldCount + 1); 
  42.  
  43.  

這里是通過前后tableview的row數量來斷言成功或者失敗。

等待

通常,在視圖切換的時候有轉場動畫,我們需要等待動畫結束,然后才能繼續,否則query的時候很可能找不到我們想要的控件。

比如,如下代碼等待VC轉場結束,當query只有一個table的時候,才繼續執行后續的代碼。

  1. [self expectationForPredicate:[NSPredicate predicateWithFormat:@"self.count = 1"
  2.  
  3.           evaluatedWithObject:app.tables 
  4.  
  5.                       handler:nil]; 
  6.  
  7. [self waitForExpectationsWithTimeout:2.0 handler:nil]; 
  8.  
  9. //后續代碼....  

Tips: 當你的UI結構比較復雜的時候,比如各種嵌套childViewController,使用XCUIElementQuery的代碼會很長,也不好維護。

另外,UI測試還會在每一步操作的時候截圖,方便對測試報告進行驗證。

查看測試結果

使用基于XCTest的框架,可以在XCode的report navigator中查看測試結果。   

 

其中:

  • Tests 用來查看詳細的測試過程
  • Coverage 用來查看代碼覆蓋率
  • Logs 用來查看測試的日志
  • 點擊圖中的紅色框指向的圖標可以看到每一步UI操作的截圖

除了利用XCode的GUI,還可以通過后文提到的命令行工具來測試,查看結果。

Stub/Mock

首先解釋兩個術語:

  • mock 表示一個模擬對象
  • stub 追蹤方法的調用,在方法調用的時候返回指定的值。

通常,如果你采用純存的XCTest,推薦采用OCMock來實現mock和stub,單元測試的三方庫通常已集成了stub和mock。

那么,如何使用mock呢?舉個官方的例子:

  1. //mock一個NSUserDefaults對象 
  2.  
  3. id userDefaultsMock = OCMClassMock([NSUserDefaults class]); 
  4.  
  5. //在調用stringForKey的時候,返回http://testurl 
  6.  
  7. OCMStub([userDefaultsMock 
  8.  
  9. stringForKey:@"MyAppURLKey"]).andReturn(@"http://testurl");  

再比如,我們要測試打開其他App,那么如何判斷確實打開了其他App呢?

  1. id app = OCMClassMock([UIApplication class]); 
  2.  
  3. OCMStub([app sharedInstance]).andReturn(app); 
  4.  
  5. OCMVerify([app openURL:url]   

使用Stub可以讓我們很方便的實現這個。

關于OCMock的使用,推薦看看objc.io的這篇文章

  • 置換測試: Mock, Stub 和其他

https://objccn.io/issue-15-5/

Quick

Quick是建立在XCTestSuite上的框架,使用XCTestSuite允許你動態創建測試用例。所以,使用Quick,你仍讓可以使用XCode的測試相關GUI和命令行工具。

使用Quick編寫的測試用例看起來是這樣子的:

  1. import Quick 
  2.  
  3. import Nimble 
  4.  
  5.   
  6.  
  7. class TableOfContentsSpec: QuickSpec { 
  8.  
  9.   override func spec() { 
  10.  
  11.     describe("the 'Documentation' directory") { 
  12.  
  13.       it("has everything you need to get started") { 
  14.  
  15.         let sections = Directory("Documentation").sections 
  16.  
  17.         expect(sections).to(contain("Organized Tests with Quick Examples and Example Groups")) 
  18.  
  19.         expect(sections).to(contain("Installing Quick")) 
  20.  
  21.       } 
  22.  
  23.   
  24.  
  25.       context("if it doesn't have what you're looking for") { 
  26.  
  27.         it("needs to be updated") { 
  28.  
  29.           let you = You(awesome: true
  30.  
  31.           expect{you.submittedAnIssue}.toEventually(beTruthy()) 
  32.  
  33.         } 
  34.  
  35.       } 
  36.  
  37.     } 
  38.  
  39.   } 
  40.  
  41.  

BDD的框架讓測試用例的目的更加明確,測試是否通過更加清晰。使用Quick,測試用例分為兩種:

單獨的用例 – 使用it來描述

it有兩個參數,

  • 行為描述
  • 行為的測試代碼

比如,以下測試Dolphin行為,它具有行為is friendly和is smart

  1. //Swift代碼 
  2.  
  3. class DolphinSpec: QuickSpec { 
  4.  
  5.   override func spec() { 
  6.  
  7.     it("is friendly") { 
  8.  
  9.       expect(Dolphin().isFriendly).to(beTruthy()) 
  10.  
  11.     } 
  12.  
  13.   
  14.  
  15.     it("is smart") { 
  16.  
  17.       expect(Dolphin().isSmart).to(beTruthy()) 
  18.  
  19.     } 
  20.  
  21.   } 
  22.  
  23.  

可以看到,BDD的核心是行為。也就是說,需要關注的是一個類提供哪些行為。

用例集合,用describe和context描述

比如,驗證dolphin的click行為的時候,我們需要兩個用例。一個是is loud,一個是has a high frequency,就可以用describe將用例組織起來。

  1. class DolphinSpec: QuickSpec { 
  2.  
  3.   override func spec() { 
  4.  
  5.     describe("a dolphin") { 
  6.  
  7.       describe("its click") { 
  8.  
  9.         it("is loud") { 
  10.  
  11.           let click = Dolphin().click() 
  12.  
  13.           expect(click.isLoud).to(beTruthy()) 
  14.  
  15.         } 
  16.  
  17.   
  18.  
  19.         it("has a high frequency") { 
  20.  
  21.           let click = Dolphin().click() 
  22.  
  23.           expect(click.hasHighFrequency).to(beTruthy()) 
  24.  
  25.         } 
  26.  
  27.       } 
  28.  
  29.     } 
  30.  
  31.   } 
  32.  
  33.  

context可以指定用例的條件:

比如

  1. describe("its click") { 
  2.  
  3.     context("when the dolphin is not near anything interesting") { 
  4.  
  5.       it("is only emitted once") { 
  6.  
  7.         expect(dolphin!.click().count).to(equal(1)) 
  8.  
  9.       } 
  10.  
  11.     } 
  12.  
  13.  

除了這些之外,Quick也支持一些切入點,進行測試前的配置:

  • beforeEach
  • afterEach
  • beforeAll
  • afterAll
  • beforeSuite
  • afterSuite

Nimble

由于Quick是基于XCTest,開發者當然可以收使用斷言來定義測試用例成功或者失敗。Quick提供了一個更有好的Framework來進行這種斷言:https://github.com/Quick/Nimble

比如,一個常見的XCTest斷言如下:

  1. XCTAssertTrue(ConditionCode, "FailReason"

在出錯的時候,會提示

  1. XCAssertTrue failed, balabala 

這時候,開發者要打個斷點,查看下上下文,看看具體失敗的原因在哪。

使用Nimble后,斷言變成類似

  1. expect(1 + 1).to(equal(2)) 
  2.  
  3. expect(3) > 2 
  4.  
  5. expect("seahorse").to(contain("sea")) 
  6.  
  7. expect(["Atlantic""Pacific"]).toNot(contain("Mississippi"))  

并且,出錯的時候,提示信息會帶著上下文的值信息,讓開發者更容易的找到錯誤。

讓你的代碼更容易單元測試

測試的準確性和工作量很大程度上依賴于開發人員的代碼質量。

通常,為了單元測試的準確性,我們在寫函數(方法)的時候會借鑒一些函數式編程的思想。其中最重要的一個思想就是

  • pure function(純函數)

何為Pure function?就是如果一個函數的輸入一樣,那么輸出一定一樣。

比如,這樣的一個函數就不是pure function。因為它依賴于外部變量value的值。

  1. static NSInteger value = 0; 
  2.  
  3.   
  4.  
  5. - (NSInteger)function_1{ 
  6.  
  7.     value = value + 1; 
  8.  
  9.     return value; 
  10.  
  11.  

而這個函數就是pure function,因為給定輸入,輸出一定一致。

  1. - (NSInteger)function_2:(NSInteger)base{ 
  2.  
  3.     NSInteger value = base + 1; 
  4.  
  5.     return value; 
  6.  
  7.  

所以,如果你寫了一個沒有參數,或者沒有返回值的方法,那么你要小心了,很可能這個方法很難測試。

關于MVC

在良好的MVC架構的App中,

  • View只做純粹的展示型工作,把用戶交互通過各種方式傳遞到外部
  • Model只做數據存儲類工作
  • Controller作為View和Model的樞紐,往往要和很多View和Model進行交互,也是自動化包括代碼維護的痛點。

所以,對Controller瘦身是iOS架構中比較重要的一環,一些通用的技巧包括:

邏輯抽離:

  • 網絡請求獨立。可以每個網絡請求以Command模式封裝成一個對象,不要直接在Controller調用AFNetworking。
  • 數據存儲獨立。建立獨立的Store類,用來做數據持久化和緩存。
  • 共有數據服務化(協議)。比如登錄狀態等等,通過服務去訪問,這樣服務提供者之需要處理服務的質量,服務使用者則信任服務提供者的結果。

Controller與View解耦合

  • 建立ViewModel層,這樣Controller只需要和ViewModel進行交互。
  • 建立UIView子類作為容器,將一些View放到容器后再把容器作為SubView添加到Controller里
  • 建立可復用的Layout層,不管是AutoLayout還是手動布局。

Controller與Controller解耦合

  • 建立頁面路由。每一個界面都抽象為一個URL,跳轉僅僅通過Intent或者URL跳轉,這樣兩個Controller完全獨立。

如果你的App用Swift開發,那么面向協議編程和不可變的值類型會讓你的代碼更容易測試。

當然,iOS組建化對自動化測試的幫助也很大,因為不管是基礎組件還是業務組件,都可以獨立測試。組建化又是一個很大的課題,這里不深入講解了。

KIF

KIF的全稱是Keep it functional。它是一個建立在XCTest的UI測試框架,通過accessibility來定位具體的控件,再利用私有的API來操作UI。由于是建立在XCTest上的,所以你可以完美的借助XCode的測試相關工具(包括命令行腳本)。

> KIF是個人非常推薦的一個框架,簡單易用。

使用KIF框架強制要求你的代碼支持accessibility。如果你之前沒接觸過,可以看看Apple的文檔

  • Accessibility Programming Guide for iOS

https://developer.apple.com/library/prerelease/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Introduction/Introduction.html

簡單來說,accessibility能夠讓視覺障礙人士使用你的App。每一個控件都有一個描述AccessibilityLabel。在開啟VoiceOver的時候,點擊控件就可以選中并且聽到對應的描述。

通常UIKit的控件是支持accessibility的,自定定義控件可以通過代碼或者Storyboard上設置。

在Storyboard上設置:   

 

  • 上面的通過Runtime Attributes設置(KVC)
  • 下面的通過GUI來設置

通過代碼設置:

  1. [alert setAccessibilityLabel:@"Label"]; 
  2.  
  3. [alert setAccessibilityValue:@"Value"]; 
  4.  
  5. [alert setAccessibilityTraits:UIAccessibilityTraitButton];  

如果你有些Accessibility的經驗,那么你肯定知道,像TableView的這種不應該支持VoiceOver的。我們可以用條件編譯來只對測試Target進行設置:

  1. #ifdef DEBUG 
  2.  
  3. [tableView setAccessibilityValue:@"Main List Table"]; 
  4.  
  5. #endif 
  6.  
  7.   
  8.  
  9. #ifdef KIF_TARGET (這個值需要在build settings里設置) 
  10.  
  11. [tableView setAccessibilityValue:@"Main List Table"]; 
  12.  
  13. #endif  

使用KIF主要有兩個核心類:

  • KIFTestCase XCTestCase的子類
  • KIFUITestActor 控制UI,常見的三種是:點擊一個View,向一個View輸入內容,等待一個View的出現

我們用KIF來測試添加一個新的ToDo

  1. - (void)testAddANewItem{ 
  2.  
  3.     [tester tapViewWithAccessibilityLabel:@"Add"]; 
  4.  
  5.     [tester enterText:@"Create a test to do item" intoViewWithAccessibilityLabel:@"Input what you want todo"]; 
  6.  
  7.     [tester tapViewWithAccessibilityLabel:@"Save"]; 
  8.  
  9.     [tester waitForTimeInterval:0.2]; 
  10.  
  11.     [tester waitForViewWithAccessibilityLabel:@"Create a test to do item"]; 
  12.  
  13.  

命令行

自動化測試中,命令行工具可以facebook的開源項目:

  • xctool

這是一個基于xcodebuild命令的擴展,在iOS自動化測試和持續集成領域很有用,而且它支持-parallelize并行測試多個bundle,大大提高測試效率。

安裝XCTool,

  1. brew install xctool 

使用

  1. path/to/xctool.sh \ 
  2.  
  3.   -workspace YourWorkspace.xcworkspace \ 
  4.  
  5.   -scheme YourScheme \ 
  6.  
  7.   -reporter plain:/path/to/plain-output.txt \ 
  8.  
  9.   run-test  

并且,xctool對于持續集成很有用,iOS常用的持續集成的server有兩個:

  • Travis CI 對于公開倉庫(比如github)免費,私有倉庫收費
  • Jenkins 免費

優化你的測試代碼

準確的測試用例

通常,你的你的測試用例分為三部分:

  • 配置測試的初始狀態
  • 對要測試的目標執行代碼
  • 對測試結果進行斷言(成功 or 失敗)

測試代碼結構

當測試用例多了,你會發現測試代碼編寫和維護也是一個技術活。通常,我們會從幾個角度考慮:

  • 不要測試私有方法(封裝是OOP的核心思想之一,不要為了測試破壞封裝)
  • 對用例分組(功能,業務相似)
  • 對單個用例保證測試獨立(不受之前測試的影響,不影響之后的測試),這也是測試是否準確的核心。
  • 提取公共的代碼和操作,減少copy/paste這類工作,測試用例是上層調用,只關心業務邏輯,不關心內部代碼實現。

一個常見的測試代碼組織如下:   

 

appium

appium采用了Client Server的模式。對于App來說就是一個Server,基于WebDriver JSON wire protocol對實際的UI操作庫進行了封裝,并且暴露出RESTFUL的接口。然后測試代碼通過HTTP請求的方式,來進行實際的測試。其中,實際驅動UI的框架根據系統版本有所不同:

  • < 9.3 采用UIAutomation
  • >= 9.3 XCUITest

原因也比較簡單:Apple在10.0之后,移除了UIAutomation的支持,只支持XCUITest。   

 

對比KIF,appium有它的優點:

  • 跨平臺,支持iOS,Android
  • 測試代碼可以由多種語言編寫,這對測試來說門檻更低
  • 測試腳本獨立與源代碼和測試框架

當然,任何框架都有缺點:

  • 自定義控件支持不好
  • WebView的支持不好

總結

由于我不是專業的iOS測試,關于測試的一點見解如下:

  • 單元測試還是選擇BDD框架,畢竟可讀性高一些,推薦Quick(Swift),Kiwi(Objective C)
  • UI測試優先推薦KIF,如果需要兼顧安卓測試,或者測試人員對OC/Swift很陌生,可以采用appium

參考資料

  • Testing with Xcode 官方文檔,關于XCTest以及XCode有詳細的講解
  • objc.io關于測試的資料對于官方文檔的補充
  • 騰訊移動品質中心 鵝廠移動品質中心,有很多好文章,強力推薦。
  • 基于 KIF 的 iOS UI 自動化測試和持續集成 美團點評技術團隊寫的一篇博客
  • testing-in-swift
  • 微信讀書排版引擎自動化測試方案 

 

責任編輯:龐桂玉 來源: iOS大全
相關推薦

2014-04-16 14:15:01

QCon2014

2012-02-27 17:34:12

Facebook自動化

2022-02-17 10:37:16

自動化開發團隊預測

2023-03-27 15:37:43

自動化測試開發

2022-05-10 11:18:42

自動化測試軟件測試

2022-06-08 14:22:55

自動化測試測試

2021-09-03 09:56:18

鴻蒙HarmonyOS應用

2013-05-16 10:58:44

Android開發自動化測試

2017-07-21 09:14:21

2022-12-04 23:52:11

iOS自動化工具

2011-12-23 17:09:57

自動化測試

2012-12-24 22:54:31

2021-06-30 19:48:21

前端自動化測試Vue 應用

2024-11-01 15:05:12

2023-06-28 15:12:33

2023-11-01 10:18:10

自動化測試工具

2022-02-16 09:01:13

iOSS開發XCode

2011-06-03 17:24:48

自動化測試

2014-11-20 13:49:15

2011-01-20 10:17:25

ibmdwWeb
點贊
收藏

51CTO技術棧公眾號

亚洲午夜色婷婷在线| 欧美日韩在线视频一区| 99久久精品久久久久久ai换脸| 久久精品黄色片| 超碰精品在线观看| 欧美日韩性视频在线| 亚洲国产日韩美| av中文字幕免费在线观看| 在线播放不卡| 在线看日韩欧美| 丰满少妇一区二区三区专区| 特一级黄色大片| 激情综合网五月| 91精品国产综合久久精品麻豆| 欧美人成在线观看| av成人手机在线| 国产精品一区久久久久| 日本成人黄色片| 美女福利视频在线观看| 伊人久久大香线蕉| 亚洲永久精品国产| 日本公妇乱淫免费视频一区三区| 久久精品视频8| 希岛爱理av免费一区二区| 欧美日本国产视频| 欧美女人性生活视频| av网址在线| 亚洲国产精品国自产拍av| 国产精品一区二区三区观看| 一级黄色大毛片| 久久九九国产| 亚洲欧美日韩在线高清直播| 欧美国产在线一区| 91精品国产66| 国产精品沙发午睡系列990531| 国产精品久久7| 国产精品伦理一区| 免费在线观看一区二区三区| 91大神在线播放精品| 久久高清无码视频| 亚洲一区二区三区无吗| 中文字幕亚洲欧美| 自拍偷拍视频亚洲| 亚洲va久久| 日韩黄色在线免费观看| 中文字幕三级电影| 伊人久久噜噜噜躁狠狠躁| 欧美精品免费视频| 国产原创精品在线| a视频在线观看| 中文字幕在线不卡国产视频| 日本一区二区精品| 国产原创av在线| 久久综合精品国产一区二区三区| 国产在线一区二区三区播放| 丰满熟妇人妻中文字幕| 国产成人日日夜夜| 69av视频在线播放| 天堂资源在线播放| 亚洲免费激情| 91精品国产一区| 成年人视频在线免费看| 亚洲影音先锋| 国产精品第100页| 亚洲精品毛片一区二区三区| 日韩精彩视频在线观看| 国产精彩精品视频| 又污又黄的网站| 精品在线播放免费| 91久色国产| 人妻丰满熟妇av无码区hd| 高清国产午夜精品久久久久久| 国产精品亚洲一区| 亚洲 小说区 图片区 都市| 麻豆精品网站| 日韩美女中文字幕| 伊人久久成人网| 狠色狠色综合久久| 97精品一区二区视频在线观看| 免费在线不卡视频| 日韩黄色小视频| 成人激情视频在线播放| 午夜精品久久久久久久91蜜桃| 亚洲欧美久久| 国产精品精品视频| 国产精品久久久国产盗摄| 国产福利一区在线观看| 国内精品久久国产| av黄色在线观看| 亚洲三级电影网站| 欧美午夜欧美| 国产日产一区二区三区| 亚洲成人久久影院| av污在线观看| h视频久久久| 国产亚洲精品日韩| 精品在线视频免费观看| 日韩成人一区二区| 国产精品theporn88| 东凛在线观看| 亚洲成人免费av| 天堂在线中文在线| 美女视频亚洲色图| 久热精品视频在线| 五月天婷婷导航| 国产suv一区二区三区88区| 欧美最大成人综合网| gogogogo高清视频在线| 一本色道久久加勒比精品| 日批视频在线看| 欧美精品系列| 91黑丝在线观看| 国产女人爽到高潮a毛片| 久热成人在线视频| 久久资源av| 欧美黄色视屏| 欧美日韩国产免费一区二区 | 亚洲精品成人久久| 国产成人精品综合久久久久99| 夜夜春成人影院| 欧美极品美女电影一区| 亚洲天堂中文在线| 91麻豆文化传媒在线观看| 国产视频不卡| 国产视频中文字幕在线观看| 色婷婷国产精品综合在线观看| 香蕉久久久久久av成人| 91亚洲国产成人久久精品| 2019av中文字幕| 国产 欧美 精品| 亚洲男人的天堂在线aⅴ视频| 福利网在线观看| 午夜无码国产理论在线| 亚洲精品不卡在线| 日本三级黄色大片| 国产不卡视频在线播放| 男人草女人视频| 国产人与zoxxxx另类91| 日韩在线播放视频| 在线免费看av片| 中文字幕av一区二区三区高| 99蜜桃臀久久久欧美精品网站| 果冻天美麻豆一区二区国产| 欧美情侣性视频| 国产成人三级一区二区在线观看一 | 久久精品影视| 成人福利网站在线观看11| 在线中文资源天堂| 欧美日韩国产首页| 91香蕉一区二区三区在线观看| 久久精品久久精品| 这里只有精品66| 亚洲区欧洲区| 91精品国产一区二区三区香蕉| 又色又爽的视频| 奇米综合一区二区三区精品视频| 日本在线一区| 尤物yw193can在线观看| 91精品国产美女浴室洗澡无遮挡| www.xx日本| 国产九色精品成人porny | 色婷婷在线影院| 天堂成人国产精品一区| 日本高清不卡一区二区三| 日韩欧美精品电影| 爽爽爽爽爽爽爽成人免费观看| 永久免费未视频| 国产在线麻豆精品观看| 欧美高清性xxxxhd | 91最新在线免费观看| 伊人春色在线观看| 亚洲高清在线观看| 国产午夜无码视频在线观看 | 国产一区二区三区综合| 国产女教师bbwbbwbbw| 美女av一区| 国产成人综合一区二区三区| 男人的天堂在线视频免费观看| 日韩午夜激情电影| 成年免费在线观看| 国产情人综合久久777777| 中文字幕成人免费视频| 激情久久一区| 日本在线观看一区二区三区| 欧美午夜在线播放| 欧美专区在线播放| 欧美一区二区三区在线观看免费| 欧美日韩精品欧美日韩精品一综合| 三级影片在线看| 久久精品一二三| 久久久久99精品成人片| 一区二区导航| 亚洲精品免费一区二区三区| 松下纱荣子在线观看| 色多多国产成人永久免费网站 | 北条麻妃99精品青青久久| 丰满熟妇乱又伦| 欧美日韩另类国产亚洲欧美一级| 国产精品成人免费一区二区视频| 久久久av毛片精品| 亚洲最大视频网| 日本va欧美va精品发布| 男人添女人下部高潮视频在观看| 国内成人自拍| 国产欧美一区二区三区另类精品| 欧美日韩免费电影| 中文字幕成人精品久久不卡| 国产 日韩 欧美 综合| 欧洲精品视频在线观看| 国产一级特黄视频| 《视频一区视频二区| 色一情一区二区三区| 亚洲精品在线二区| 久久最新免费视频| 成人三级视频| 欧美精品一区二区三区四区五区| caoporn成人| 91久久精品美女| 色综合天天色| 欧美中文在线字幕| av电影在线免费| 欧美成人在线免费视频| 成人高潮成人免费观看| 精品亚洲国产成av人片传媒| 亚洲伦理在线观看| 欧美一区二区三区精品| 少妇无套内谢久久久久| 色琪琪一区二区三区亚洲区| 国产污片在线观看| 亚洲综合视频在线观看| 亚洲综合网在线| 中文字幕综合网| 小嫩苞一区二区三区| 国产日韩成人精品| 加勒比综合在线| 久久久久99精品一区| 亚洲色图14p| 91免费视频大全| 青青草成人免费视频| av在线综合网| 国产麻豆xxxvideo实拍| 成人免费高清视频在线观看| 精品国产一二区| 粉嫩一区二区三区在线看| 精品无码av一区二区三区不卡| 国产精品一区二区久久精品爱涩| 久久久久久久久久一区二区| 久久99国内精品| 中文字幕1234区| 极品美女销魂一区二区三区| av噜噜在线观看| 国产麻豆成人精品| 女人扒开双腿让男人捅| 岛国精品一区二区| 这里只有精品在线观看视频| 秋霞成人午夜伦在线观看| 美女网站免费观看视频| 日本免费新一区视频| 黄大色黄女片18第一次| 国产一区二区中文字幕| 野花视频免费在线观看| 成人av网站在线观看| 精品无码在线视频| 中文字幕精品综合| 羞羞在线观看视频| 亚洲激情在线激情| 成人激情五月天| 粉嫩蜜臀av国产精品网站| 性感美女一区二区三区| 99视频在线精品| 91成人破解版| 亚洲女与黑人做爰| 久久久久久蜜桃| 色欧美片视频在线观看在线视频| 亚洲天堂视频在线| 精品国产露脸精彩对白| 日韩黄色影片| 亚洲成人av资源网| 青青国产在线| 久久久精品影院| 国产自产自拍视频在线观看| 久久av.com| 川上优av中文字幕一区二区| 国产成人免费91av在线| 91精品麻豆| 乱一区二区三区在线播放| 欧美xxav| 成年人视频观看| 国内精品久久久久久久影视蜜臀| 人妻av中文系列| 久久99精品国产.久久久久久| 日本在线不卡一区二区| 欧美激情一区二区三区不卡| 久草视频手机在线观看| 在线免费观看成人短视频| 精品久久久久久亚洲综合网站| 日韩精品视频免费在线观看| 欧美成人二区| 热久久这里只有| 亚洲大奶少妇| 一卡二卡3卡四卡高清精品视频| 黄色成人在线网址| 中文字幕免费高清在线| 99精品国产热久久91蜜凸| 亚洲熟女毛茸茸| 疯狂欧美牲乱大交777| 国产999久久久| 国产亚洲欧美另类中文| av福利导福航大全在线| 91亚洲国产精品| 禁断一区二区三区在线| 成人性免费视频| 夜夜夜久久久| 中文字幕第三区| 国产日韩精品久久久| 国产成人无码精品亚洲| 欧美一区二区播放| jizz在线观看中文| 欧美最猛性xxxxx亚洲精品| 日韩欧美久久| 99re99热| 日本不卡在线视频| 久久精品国产亚洲av麻豆| 亚洲国产三级在线| www.国产黄色| 操人视频在线观看欧美| 国产综合色激情| 日韩欧美电影一区二区| 久久aⅴ国产紧身牛仔裤| 超碰caoprom| 亚洲自拍偷拍欧美| 国产sm主人调教女m视频| 中文字幕亚洲欧美一区二区三区| 肉色欧美久久久久久久免费看| 国产成人97精品免费看片| 老司机aⅴ在线精品导航| 男人天堂av片| 风间由美性色一区二区三区| 久草视频手机在线| 91精品国产综合久久久久| 蜜桃视频网站在线观看| 成人羞羞国产免费| 亚洲国产一区二区在线观看 | 亚洲精品国产嫩草在线观看| 欧美极品视频一区二区三区| 国产亚洲在线| av网站免费在线播放| 色综合久久久久综合| 国产一级片在线播放| 国产精品久久久久久久av大片| 国产在线观看91一区二区三区| 欧美日韩怡红院| 亚洲国产岛国毛片在线| 一级特黄aa大片| 久久香蕉频线观| www.豆豆成人网.com| 成人精品视频在线播放| 99久久综合精品| 四虎成人在线观看| 在线免费观看羞羞视频一区二区| 欧美成人一二区| 日韩video| 成人国产精品免费观看视频| 日本三级小视频| 亚洲视频欧洲视频| 国产电影一区二区三区爱妃记| 亚洲精品国产精品国自产| 精品一区二区精品| 久草视频在线资源| 精品香蕉一区二区三区| 成人黄色免费网站| 成人短视频在线看| 成av人片一区二区| www.国产毛片| 日韩一区二区三区四区| 国产福利在线免费观看| 精品国产一区二区三区四区vr| 久久国产免费| 欧日韩不卡视频| 精品不卡在线视频| 欧洲av一区二区| 欧美交换配乱吟粗大25p| 99精品欧美一区二区蜜桃免费| 天天干,天天干| 欧美成人激情在线| 一道在线中文一区二区三区| 欧美wwwwwww| 午夜欧美2019年伦理| 在线激情免费视频| 成人免费观看网站| 日本不卡在线视频| 国语对白一区二区| 中文字幕日韩在线视频| 国产欧美三级电影| 日本激情综合网| 亚洲高清视频在线| 免费**毛片在线| 欧美亚洲国产免费| 成人高清在线视频| 一区二区三区精|