iOS AutomationXCUITest + Gherkin
• Technical Lead iOS Engineer @ PropertyGuru• Agile, Xtreme Programming• Tests• Calabash-iOS ----> XCUITest• Demo [https://github.com/depoon/WeatherAppDemo]
Agenda
1. Introduction XCUITest2. Building a simple Test Suite3. Gherkin for XCUITest
XCUITest• Introduced in Xcode 7 in 2015• xUnit Test Cases (XCTestCase)• UITest targets cant see production codes
• Builds and tests are all executed using XCode (ObjC/Swift)
• Record functionality1. Introduction (iOS Automation Tools, XCUITest)
XCUITest - Recording
1. Introduction (iOS Automation Tools, XCUITest)
[Source] http://blog.xebia.com/automated-ui-testing-with-react-native-on-ios/
XCUITest - XCUIElement
1. Introduction (iOS Automation Tools, XCUITest)
XCUIElement //Elements are objects encapsulating the information needed to dynamically locate a user interface.element in an application. Elements are described in terms of queries [Apple Documentation - XCTest]
let app = XCUIApplication() //Object that queries views on the appapp.staticTexts //returns XCUIElementQuery “collection” representing “UILabels”app.buttons //returns XCUIElementQuery “collection” of representing “UIButtons”app.tables //returns XCUIElementQuery “collection” of representing “UITables”
app.tables.staticTexts //returns XCUIElementQuery “collection” representing “UILabels” which has superviews of type “UITable”
app.staticTexts[“Hello World”] //returns a label element with accessibilityIdentifier or text value “Hello World”
app.tables[“myTable”] //returns a table element with accessibilityIdentifier “myTable”
1. Introduction (iOS Automation Tools, XCUITest)
groups disclosureTriangles tabGroups sliders images menus
windows popUpButtons toolbars pageIndicator
s icons menuItems
sheets comboBoxes statusBars progressIndicators searchFields menuBars
drawers menuButtons tables activityIndicators scrollViews menuBarItem
s
alerts toolbarButtons tableRows segmentedCo
ntrols scrollBars maps
dialogs popovers tableColumns pickers staticTexts webViewsbuttons keyboards outlines pickerWheels textFields steppers
radioButtons keys outlineRows switches secureTextFields cells
radioGroups navigationBars browsers toggles datePickers layoutAreas
checkBoxes tabBars collectionViews links textViews otherElement
sapp.otherElements //returns [ elements ] of UIView
XCUITest - XCUIElement
XCUITest - Interactions
1. Introduction (iOS Automation Tools, XCUITest)
let app = XCUIApplication() //Object that queries views on the appapp.staticTexts[“Hello World”].exists //returns true if element exists
app.buttons[“Save”].tap() //taps the “Save” button
app.tables[“myTable”].swipeUp() //swipes up the table
app.textFields[“myField”].typeText(“John Doe”) //types value in textField
Other interactions: pinchWithScale, pressForDuration, doubleTap()
XCTAssertTrue(app.staticTexts[“Hello World”].exists) //Throws exception if label does not exists
Requirements
2. Building a simple test suite
Create a Weather App1. Given I am at the City Search Screen
When I search for a valid city (eg “London”)Then I should see a weather details page of that city
2. Given I am at the City Search ScreenWhen I search for an invalid city (eg “NotACity”)Then I should see an error message
Here’s what we built
2. Building a simple test suite
Creating a UITest Target
2. Building a simple test suite
Recording - Valid City
2. Building a simple test suite
Recording - Invalid City
2. Building a simple test suite
Generated Code - Valid City
2. Building a simple test suite
func testUserAbleToSearchForValidCity() { let app = app2 app.searchFields["Search a city"].tap() app.searchFields["Search a city"] let app2 = app app2.buttons["Search"].tap() app.searchFields["Search a city"]
let tablesQuery = app2.tables tablesQuery.staticTexts["London"].tap() tablesQuery.staticTexts["53"].tap() tablesQuery.staticTexts["Partly Cloudy"].tap() }
Generated Code - Invalid City
2. Building a simple test suite
func testUserSeesErrorMessageForInvalidCity() { let app = XCUIApplciation() app.searchFields["Search a city"].tap() app.searchFields["Search a city"] app.buttons["Search"].tap()
app.searchFields["Search a city"] let errorAlert = app.alerts["Error"]
errorAlert.staticTexts["Error"].tap()errorAlert.staticTexts[
"Unable to find any matching weather location to the query submitted!”].tap()errorAlert.collectionViews["OK"].tap()
}
2. Building a simple test suite
func testUserAbleToSearchForValidCity() { let app = XCUIApplication() let searchField = app.searchFields["Search a city"]
searchField.tap() searchField.typeText("London") app.buttons["Search"].tap() self.userWaitsToSeeText("London") self.userWaitsToSeeText("53") self.userWaitsToSeeText("Partly Cloudy”)
self.waitForExpectationsWithTimeout(5, handler: nil) }
private func userWaitsToSeeText(text: String){ self.expectationForPredicate( NSPredicate(format: "exists == 1"), evaluatedWithObject: XCUIApplication().tables.staticTexts[text], handler: nil )
// XCUIApplication().tables.staticTexts[text].exists <— boolean }
Refactored Code
2. Building a simple test suite
func testUserSeesErrorMessageForInvalidCity() { let app = XCUIApplication() let searchField = app.searchFields["Search a city”]
searchField.tap() searchField.typeText("NotACity") app.buttons["Search"].tap() self.userWaitsToSeeText("Error") self.userWaitsToSeeText(
"Unable to find any matching weather location to the query submitted!" )
self.waitForExpectationsWithTimeout(5, handler: nil)
self.userTapsOnAlertButton("OK") }
private func userTapsOnAlertButton(buttonTitle: String){ XCUIApplication().buttons[buttonTitle].tap() }
Refactored Code
2. Building a simple test suite
Scheme Management
2. Building a simple test suite
Scheme Management
Gherkin
3. Use Gherkins
(User Registration)Given I am at the user registration page
When I enter invalid email addressAnd I tap “Register“Then I should see “Invalid Email Address”
Gherkin - Example 1
3. Use Gherkins
(Shopping with existing Items in shopping cart)Given I have 1 Wallet and 1 Belt in my shopping cartAnd I am at the Shopping Item Details Page for a
Bag
When I select quantity as “1”And I tap on “Add to Shopping Cart”
Then I should be at Shopping Cart ScreenAnd I should see “3” total items in my shopping cart
Gherkin - Example 2
3. Use Gherkins
2. Building a simple test suite
Given I am at Weather Search Form Page
When I enter city search for “London”
Then I should be at Weather Details Page And I wait to see “London”And I wait to see “53”
Gherkin - Our Acceptance Tests
2. Building a simple test suite
Given I am at Weather Search Form Page
When I enter city search for “NotACity”
Then I wait to see “Error”And I wait to see “Unable to …”And I tap on alert button “OK”
Gherkin - Our Acceptance Tests
3. Use Gherkins
• Language used in Behavioural Driven Development (BDD) to specify software requirements with examples
• Executable, facilitate collaboration with non developers
• Lets add Gherkin to our project in Xcodehttps://github.com/net-a-porter-mobile/XCTest-Gherkinpod 'XCTest-Gherkin'
Gherkin
3. Use Gherkins[Source] https://github.com/net-a-porter-mobile/XCTest-Gherkin
Gherkin
3. Use Gherkins
Given I am at Weather Search Form PageWhen I enter city search for “London”Then I should be at Weather Details Page And I wait to see “London”And I wait to see “53”
step("I (am|should be) at Weather Search Form Page”) {let weatherSearchPage =
WeatherSearchPage(self.test)}
struct WeatherSearchPage{init(testCase: XCTestCase){
testCase.expectationForPredicate(NSPredicate(format: “exists == 1”),evaluatedWithObject:
XCUIApplication().otherElements[“WeatherSearchPage”],handler: nil
)testCase.waitForExpectationsWithTimeout(5, handler: nil)
}}
3. Use Gherkins
Given I am at Weather Search Form ScreenWhen I enter city search for “London”Then I should be at Weather Details Page And I wait to see “London”And I wait to see “53”
step("I enter city search for \”(.*?)\””) { (matches: [String]) inlet weatherSearchPage = WeatherSearchPage(testCase:
self.test)weatherSearchPage.userSearchForCity(matches.first!)
}
struct WeatherSearchPage{func userSearchForCity(city: String){
let app = XCUIApplication()let searchField = app.searchFields[“Search a
city”]searchField.tap()searchField.typeText(city)app.buttons[“Search”].tap()
}}
3. Use Gherkins
Given I am at Weather Search Form PageWhen I enter city search for “London”Then I should be at Weather Details Page And I wait to see “London”And I wait to see “53”
step("I (am|should be) at Weather Details Page”) {WeatherDetailsPage(testCase: self.test)
}
struct WeatherDetailsPage{init(testCase: XCTestCase){
testCase.expectationForPredicate(NSPredicate(format: “exists == 1”),evaluatedWithObject: XCUIApplication().otherElements[“Weather
Forecast”],handler: nil
)testCase.waitForExpectationsWithTimeout(5, handler: nil)
}}
3. Use Gherkins
Given I am at Weather Search Form PageWhen I enter city search for “London”Then I should be at Weather Details Page And I wait to see “London”And I wait to see “53”
step("I wait to see \”(.*?)\””) { (matches: [String]) inself.test.expectationForPredicate(
NSPredicate(format: “exists == 1”),evaluatedWithObject:
XCUIApplication().staticTexts[matches.first!],handler: nil
)self.test.waitForExpectationsWithTimeout(5, handler: nil)
}
3. Use Gherkins
Given I am at Weather Search Form PageWhen I enter city search for “NotACity”
Then I wait to see “Error”Then I wait to see “Unable to …”Then I tap on alert button “OK”
step("I tap on alert button \”(.*?)\””) {XCUIApplication().buttons[matches.first!].t
ap()}
3. Use Gherkins
Using an additional pod to simply statements - https://github.com/joemasilotti/JAMTestHelper
Gherkin
3. Use Gherkins
Using an additional pod to simply statements - https://github.com/joemasilotti/JAMTestHelper
Gherkin
3. Use Gherkins
Gherkin
3. Use Gherkins
Gherkin
3. Use Gherkins
Gherkin
Gherkin
3. Use Gherkins
Gherkin
3. Use Gherkins
Gherkin - Using Feature File
3. Use Gherkins
Why should I use Gherkin with XCUITest?
3. Use Gherkins
• Acceptance Test Driven Development• Cross platform testing is still possible without need for
3rd party tools• Objective C + Swift
Questions?