page object in xcuitest

70

Upload: jz-chang

Post on 14-Jan-2017

219 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: Page Object in XCUITest
Page 2: Page Object in XCUITest

About Us• Nadia Lin

• Software Engineer in Test in the KKBOX Inc

• Computer vision (openCV)

• Mark Chang

• Software Engineer in Test in the KKBOX Inc

• 🐴 的學習筆記 Blog

• Github markchangjz

Page 3: Page Object in XCUITest

Overview• Introduce Page Object Pattern

• Implement Page Classes (Swift 2.3)

• Strategies and Tricks

• Encountered Problems

• Q & A

Page 4: Page Object in XCUITest

What is Page Object?

Reference by Martin Fowler

Page 5: Page Object in XCUITest

Cases in manual test

Page 6: Page Object in XCUITest

Cases in manual test• Case1:

• Home → Chat with people → Say “Hello”

• Case2:

• Home → Swipe to delete message

Facebook Messenger

Page 7: Page Object in XCUITest

Cases in automation test

Page 8: Page Object in XCUITest

Cases in automation test

class UITests: XCTestCase { let app = XCUIApplication() func testChatWithPeople() { app.buttons["Home"].tap() app.cells["Nadia"].tap() app.textFields["Your message"].typeText("Hello") XCTAssertTrue(……) } }

Case 1:

Home → Chat with people → Say “Hello”

Page 9: Page Object in XCUITest

Cases in automation test

class UITests: XCTestCase { let app = XCUIApplication() func testDeleteMessage() { app.buttons["Home"].tap() app.cells["Nadia"].swipeLeft() app.buttons["Delete"].tap() XCTAssertTrue(……) } }

Case 2:

Home → Swipe to delete message

Page 10: Page Object in XCUITest

New SPEC Coming

Page 11: Page Object in XCUITest

Modify your cases

class UITests: XCTestCase { let app = XCUIApplication() func testChatWithPeople() { app.buttons[“My Messages"].tap() app.cells["Nadia"].tap() app.textFields["Your message"].typeText("Hello") XCTAssertTrue(……) } }

Case 1:

My Messages → Chat with people → Say “Hello”

Page 12: Page Object in XCUITest

Modify your cases

class UITests: XCTestCase { let app = XCUIApplication() func testChatWithPeople() { app.buttons[“My Messages"].tap() app.cells["Nadia"].tap() app.textFields["Your message"].typeText("Hello") XCTAssertTrue(……) } }

Case 2:

My Messages → Swipe to delete message

Page 13: Page Object in XCUITest

class UITests: XCTestCase { let app = XCUIApplication() func testChatWithPeople() { app.buttons["My Messages"].tap() app.cells["Nadia"].tap() app.textFields["Your message"].typeText("Hello") XCTAssertTrue(……) } func testDeleteMessage() { app.buttons["My Messages"].tap() app.cells["Nadia"].swipeLeft() app.buttons["Delete"].tap() XCTAssertTrue(……) } }

Something wrong?

Hard to maintain

Page 14: Page Object in XCUITest

class UITests: XCTestCase { let app = XCUIApplication() func testChatWithPeople() { app.buttons["My Messages"].tap() app.cells["Nadia"].tap() app.textFields["Your message"].typeText("Hello") XCTAssertTrue(……) } func testDeleteMessage() { app.buttons["My Messages"].tap() app.cells["Nadia"].swipeLeft() app.buttons["Delete"].tap() XCTAssertTrue(……) } }

Hard to read

Something wrong?

Page 15: Page Object in XCUITest

class UITests: XCTestCase { let app = XCUIApplication() func testChatWithPeople() { app.buttons["My Messages"].tap() app.cells["Nadia"].tap() app.textFields["Your message"].typeText("Hello") XCTAssertTrue(……) } func testDeleteMessage() { app.buttons["My Messages"].tap() app.cells["Nadia"].swipeLeft() app.buttons["Delete"].tap() XCTAssertTrue(……) } }

Duplicate code

Something wrong?

Page 16: Page Object in XCUITest

So, What is Page Object?

Reference by Martin Fowler

Page 17: Page Object in XCUITest

The advantage of Page Object

Easy to maintainEasy to read code

Reduce Duplicate code

Page 18: Page Object in XCUITest

Page

Page 19: Page Object in XCUITest

Page

Page 20: Page Object in XCUITest

Page

Page 21: Page Object in XCUITest

Page

Page 22: Page Object in XCUITest

Page

Page 23: Page Object in XCUITest

Tab BarHome PageCalls PageGroup PagePeople PageMe Page

Page 24: Page Object in XCUITest
Page 25: Page Object in XCUITest

How to know if is on this page or element ready?

Wait

Page 26: Page Object in XCUITest

Wait

Page 27: Page Object in XCUITest

Wait for page loadedimport XCTest

class Page { static let app = XCUIApplication() private func waitForPageLoaded() { } required init() { waitForPageLoaded() } }

Page 28: Page Object in XCUITest

Predicateclass HomePage: Page { override func waitForPageLoaded() { let homeButton = app.buttons["Home"] let exists = NSPredicate(format: "exists == true") expectation(for: exists, evaluatedWithObject: homeButton, handler: nil) waitForExpectations(timeout: 5, handler: nil) } }

Reference

Page 29: Page Object in XCUITest

func testChatWithPeople() { let chatRoomPage = homePage.chat(with: "Nadia").sendMessage("Hello") XCTAssertEqual(chatRoomPage.leatestMessage, "Hello") }

Reduce duplicate code

app.cells[“Nadia”].tap()

Page 30: Page Object in XCUITest

func testChatWithPeople() { let chatRoomPage = homePage.chat(with: "Nadia").sendMessage("Hello") XCTAssertEqual(chatRoomPage.leatestMessage, "Hello") }

Reduce duplicate code func chat(with name: String) -> ChatRoomPage { app.cells[name].tap() return ChatRoomPage() }

Page 31: Page Object in XCUITest

func testChatWithPeople() { let chatRoomPage = homePage.chat(with: "Nadia").sendMessage("Hello") XCTAssertEqual(chatRoomPage.leatestMessage, "Hello") }

Reduce duplicate code

app.textFields["Your message"].typeText("Hello")

Page 32: Page Object in XCUITest

func testChatWithPeople() { let chatRoomPage = homePage.chat(with: "Nadia").sendMessage("Hello") XCTAssertEqual(chatRoomPage.leatestMessage, "Hello") }

Reduce duplicate code

func sendMessage(msg: String) -> ChatRoomPage { app.textFields["Your message"].typeText(msg) return ChatRoomPage() }

Page 33: Page Object in XCUITest

HomePage

- newMessageButton: XCUIElement

- userCell(index: Int): XCUIElement - waitForPageLoaded(): Void + getRecentMessage(index: Int): String + chat(with name: String): ChatPage + goToNewMessagePage(): NewMessagePage + goToSearchPage(): SearchPage + goToHomePage(): HomePage + goToCallsPage(): CallsPage + goToGroupsPage(): GroupsPage + goToPeoplePage(): PeoplePage + goToMePage(): MePage

Page 34: Page Object in XCUITest

Page Object Pattern

Chat Room Page

send message

Home Page

chat

back

People Page

chatgo to people page

Search Page

go to search page

search

go to search page

Page 35: Page Object in XCUITest

Strategies & Tricks

• Protocol Extensions • Tab Bar

• Search Bar

• Generics • Go Back

• Go To Different Page

Page 36: Page Object in XCUITest

Tab Bar

Page 37: Page Object in XCUITest

Tab Bar

protocol MessengerTabBar { func goToHomePage() -> HomePage func goToCallsPage() -> CallsPage func goToGroupsPage() -> GroupsPage func goToPeoplePage() -> PeoplePage func goToMePage() -> MePage }

Page 38: Page Object in XCUITest

Tab Barprotocol MessengerTabBar { // ... }

extension MessengerTabBar { private var homeButton: XCUIElement { return Page.app.buttons["Home"] } // ... func goToHomePage() -> HomePage { homeButton.tap() return HomePage() } // ... }

Page 39: Page Object in XCUITest

Tab Barfinal class HomePage: Page, MessengerTabBar { // ... } HomePage

Page 40: Page Object in XCUITest

let homePage = HomePage()

homePage.gotoGroupsPage() .gotoMePage()

Page 41: Page Object in XCUITest

let homePage = HomePage()

homePage.goToGroupsPage() .gotoMePage()

Page 42: Page Object in XCUITest

let homePage = HomePage()

homePage.goToGroupsPage() .goToMePage()

Page 43: Page Object in XCUITest

Protocol Extensions• There are have MessengerSearchBar

Page 44: Page Object in XCUITest

Protocol Extensions

Chat Room Page

send message

Home Page

chat

back

People Page

chatgo to people page

Search Page

go to search page

search

go to search page

Page 45: Page Object in XCUITest

Protocol Extensionsfinal class HomePage: Page, MessengerTabBar, MessengerSearchBar { // ... }

final class GroupsPage: Page, MessengerTabBar, MessengerSearchBar { // ... }

final class MePage: Page, MessengerTabBar, MessengerSearchBar { // ... }

final class PeoplePage: Page, MessengerTabBar, MessengerSearchBar { // ... }

Page 46: Page Object in XCUITest

Back

Page 47: Page Object in XCUITest

HomePage

backToHomePage()

backToPeoplePage()

PeoplePage ChatRoomPage

Back

Page 48: Page Object in XCUITest

Back

Chat Room Page

send message

Home Page

chat

back

People Page

chatgo to people page

Search Page

go to search page

search

go to search page

Page 49: Page Object in XCUITest

final class ChatRoomPage: Page {

private let backButton = Page.app.buttons[“Back”] // ... func backToHomePage() -> HomePage { backButton.tap() return HomePage() } func backToPeoplePage() -> PeoplePage { backButton.tap() return PeoplePage() } // ... }

Back

Page 50: Page Object in XCUITest

final class ChatRoomPage: Page {

private let backButton = Page.app.buttons[“Back”] // ... func backTo<T: Page>(type: T.Type) -> T { backButton.tap() return type.init() }

// ... }

Back

Generics

Page 51: Page Object in XCUITest

HomePage ChatRoomPage

1. chat(with:" ")

2. backTo(HomePage)

homePage.chat(with:" ").backTo(HomePage)

Page 52: Page Object in XCUITest

Taps the same button,

but it could go to different page

Generics

Can we return two page classes !?

Page 53: Page Object in XCUITest

GenericsTap play button Possible 1

Go to Nowplaying pagePossible 2

Go to Chart page

Page 54: Page Object in XCUITest

Genericsfunc playPlaylistAndExpectTransitionToPage<T: Page>(type: T.Type) -> T { //… playButton.tap()

return type.init() }

Possible 1. Go to Nowplaying page chartPage.playPlaylistAndExpectTransitionToPage(NowplayingPage)

Possible 2. Go to Chart page chartPage.playPlaylistAndExpectTransitionToPage(ChartPage)

Page 55: Page Object in XCUITest

Encountered Problems

Page 56: Page Object in XCUITest

Sets Element Accessibility• Accessibility data makes UI testing possible

Reference: UI Testing in Xcode - WWDC 2015 - Videos - Apple Developer

Test

abili

ty

Quality of Accessibility Data

Page 57: Page Object in XCUITest

Speed Up Testing• Set launch arguments to speed up testing

Tutorial View

Page 58: Page Object in XCUITest

Speed Up Testingoverride func setUp() { //… let app = XCUIApplication() app.launchArguments.append("-forceDoNotShowTutorial") app.launchArguments.append("1") app.launch() //… }

NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults]; if ([standardDefaults boolForKey:@"forceDoNotShowTutorial"]) { [UserConfig sharedInstance].everShowTutorial = YES; }

• In Test Code (Swift 2.3) - Set launch arguments

• In App Code (Objective-C) - Read launch arguments

Page 59: Page Object in XCUITest

Reduce Motion• UI animation might cause test fail while transit

Page 60: Page Object in XCUITest

Reduce Motion• Settings > General > Accessibility > Reduce Motion

• UIAccessibilityIsReduceMotionEnabled()

• Returns a Boolean value indicating whether reduce motion is enabled (API Reference)

• true if the user has enabled Reduce Motion in Settings; otherwise, false

Page 61: Page Object in XCUITest

Turn Off Auto-Correction• Input string might be changed

• Settings > General > Keyboards

Page 62: Page Object in XCUITest

Slow Network Speed• Using Network Link Conditioner tool to restrict

network speed. (For simulator)

Page 63: Page Object in XCUITest

Software Update• Don’t remind me to upgrade iOS ( Tutorial Link )

Page 64: Page Object in XCUITest

Recap• Make your page object at the user level,

rather than expose implement details

• Protocol Extensions: Extract common actions

• Generics: Go to indicated page

• iOS-Messenger-Page-Object

Page 65: Page Object in XCUITest
Page 66: Page Object in XCUITest

Q & A• Q: ⼤概你們就是把⼀個,⽤ state machine 的概念,配合 IoC 反轉注⼊的概念,把這個精神套⼊到你的 UI test 這個精神、這個動作上⾯。那我想要問的就是,你們費了這麼多功夫,實現了這個 page object,你覺得它有什麼缺點,會不會變成說你做了⼀個這麼精緻的 page object 的 test,會不會變成我還要再寫⼀個測試程式去測這個測試程式?

• A: (Mark) Page object ⽬前我們實作起來,看起來是 ... 缺點來說 我到⽬前都覺得滿順利的,因為我們⼀開始在實作的時候,其實都是⽤ 直接點元件 這樣點 這樣點 然後就其實也看不出來測試到底在測試什麼東西,然後你再包裝成 page object 的話,就可讀性都⽐較⾼,⾄於說包裝起來是不是要測試,我們⽬前是沒有這⼀層的測試,不然這樣其實也測不完,所以我們這部份是沒有再繼續對 page object 這個 pattern 去做測試。

Page 67: Page Object in XCUITest

Q & A• Q: 那你怎麼 guarantee 說你的 page object 是沒有問題的。

• A: (Nadia) 我覺得這個問題感覺像是說,你怎麼確定你寫的測試是正確的? (對) 但是,既然我們寫了⾃動化測試,我們就要對它有⼀定的⾃信 (ok),不然永遠都⼿動測試就好了,我們適時要相信⼀下機器,我們是這麼想的。

• Q: (Nick) 我想請問⼀下,現在就是從五⽉開始到現在,花了半年多的時間,所以現在寫的測試是真的有投⼊到現⾏的 project 上嗎?

• A: (Nadia) 我們現在是積極地在擴充我們的 code coverage,應該差不多可以放上去,只是我們有某些 feature 的地⽅還沒有實作到⾃動化測試,所以還不敢讓它當做真正的測試結果。

• Q: (Nick) 第⼆個問題是,我知道 KKBOX 有 support iOS 跟 Android 的 app,你們之前可能想要做⼀個⾃動化測試的 ... 應該說通吃兩個平台,那你們現在是之後打算會拆開嗎?

• A: (Nadia) 對,我們現在要拆開。

• Q: (Nick) 對,這點可能就是滿好奇的,因為我想說現在滿多⼈ 有些公司 ⽐如說測試⼈員沒有那麼多,他們會去找能夠跨平台的⼯具,像 Appium 或 Calabash 這種東西,那我覺得拆開這件事情,其實我之前也有想過,因為我覺得拆開有它的好處,我想說滿想聽聽看不知道可不可以分享,為什麼最後決定要拆開? (Zonble: Jeremy 要不要上⼀下? 做決定的⼈在這裡...)

Page 68: Page Object in XCUITest

Q & A• A: (Jeremy) 或許⼤家都知道 KKBOX 之前在公開的場合有 share 我們在使⽤ Appium + Robot

Framework 之類的 tool,要在同⼀個 case 裡⾯通知所有的平台,那當然⾮常理想,不過相信各位有⽤過 Appium 的,會發覺其實它三不五⼗會壞掉,就是沒那麼穩定,那其實各位如果有⼀直在追 testing tool 的話,你會發覺這⼀年多來,其實官⽅的動作越來越⼤,為什麼在這之前所有 3rd party 的 tool ⽐如說剛提到的 Calabash、Robotium ⼀⼤堆,你會發覺好多的 project,好多都已經 deprecated、不再 maintain 了,其實我們也漸漸看到這個趨勢,其實以最近的 case 來講,XCUITest 在去年就已經推出了,可是 Appium 到今年好像... 不知道幾個⽉前才整完,所以在這之前你都要忍受被 Apple 放棄的 UIAutomation 時不時這裡壞、那裡壞的問題,我們是看到這個趨勢,那我們會覺得說,應該要重新思考⼀下,因為畢竟時空環境已經不⼀樣了,再來就是我們希望 QA 的開發⼯作能夠跟 RD 的開發⼯作能夠結合得更緊密⼀點,⽐如說以前我們⽤ Appium 或許 RD 還要花時間去學,你⽤ Python 寫、你⽤ Ruby 寫,就是跟他們的 tool 是不⼀樣的,那其實在 GTAC 有幾個場次也提到 automation 實作的⽅式,其實最好是跟著 RD 的語⾔下去做,所以當他今天要進來協助你的時候,他是可以直接協助的,最主要有這幾點考量。就是官⽅的動作已經不是 3rd party 可以追得上的,然後再來就是跟 RD 的 cowork,那透過這樣的⽅式你會得到更多 official 的 support,然後像 Appium 以前很難算 code coverage,我們 adopt XCUITest 之後,testing tool 就可以直接算,那或許我們在做 E2E testing 算 coverage 沒有那麼直接的效益,可是我們⾄少知道說哪個⾯向,因為我們看得出來說哪些 function 被 cover 得⽐較多,所以我們下⼀個在做的時候就會往另⼀個⽅向去做,讓每⼀個 function 都能夠被 cover 到。那包括我們的 Android 現在也是⽤ Espresso,也都有這⽅⾯的好處。

Page 69: Page Object in XCUITest

Q & A• Q: (Hanyu) 因為我也有寫 UI testing,我⼀直很好奇⼀點就是,因為 UI testing 不能在 Jenkins 上跑,它要在 Mac server 上跑,請問你們怎麼解決這個問題?

• A: (Zonble) 我們在 Mac server 上跑。

• Q: (Hanyu) 可是這樣⼦你們就,我不知道怎麼串接、那個架構是怎麼樣,我滿好奇的,因為我知道你們好像是⼀台 Jenkins 然後掛 4、5 台 Mac server 吧。

• A: (Zonble) iOS team 的 Jenkins 跟 QA team 其實分開,有分開來的機器,然後兩個部⾨間再互相 sync。你在我們部落格看到的我那篇⽂章講我們⾃⼰的 build system,然後我們會 build ... 那台機器在 iOS team 這邊主要在 build 出 daily build,幾乎我們 RD 每個 branch 都有編出⾃⼰的,幾乎每個 revision 都編出⼀個版本,然後同時在執⾏,像我們整個 app 有 4、5 個⾯向,app 裡頭的播放器、跟 server 的溝通,那邊寫了⼀⼤堆的單元測試,在 Jenkins 上我們主要跑這些,那今年轉到 XCUITest 那 Jeremy 他們就 study 這些,看起來那個 Jenkins 跑這個東西就是跑得不順,就另外再去架了那個 Xcode Server。然後 Xcode Server 就定時去拉新的 code 然後在上⾯執⾏。

Page 70: Page Object in XCUITest

Reference• Page Object

• DSL, Page Object and Selenium – a way to reliable functional tests

• Best Practices - SELENIUM DOCUMENTATION

• PageObjects · SeleniumHQ_selenium Wiki · GitHub

• XCUITest

• UI Testing Cheat Sheet and Examples · masilotti.com

• XCTest and the Page Object Model

• Swift

• Getting Started with Swift