ui test
xcode
Exists
Table of contents
- Tap on elements
- Tap Element
- Wait For Element Before Tapping
- Force Tap On Element
- Swipe
- Text Input
- Type
- Clear And Enter Text
- Enter Text
- Alerts
- Dismissing Alerts
- Dismissing Action Sheets
- System alerts within the application
- Sliders
- Pickers
- Date picker
- Web Links
- Case Study
Tap on elements
Tap Element
1
| app.staticTexts["Apple"].tap()
|
Wait For Element Before Tapping
1
2
3
4
5
6
7
8
9
10
11
| // File: XCTestExtensions > XCUIElementExtensions.swift
import XCTest
extension XCUIElement {
// Custom method to wait before tapping on element
func tapElement() {
waitForElementToBecomeHittable(timeout: .medium)
tap()
}
}
|
Force Tap On Element
Work around for XCUITest Bug to make certain elements tappable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // File: XCTestExtensions > XCUIElementExtensions.swift
import XCTest
extension XCUIElement {
func forceTap() {
if isHittable {
tap()
} else {
coordinate(withNormalizedOffset: CGVector(dx: 0.0, dy: 0.0)).tap()
}
}
}
|
Swipe
1
2
3
4
5
6
7
8
9
10
11
12
13
| let element = app.collectionViews["Apple"]
// Swipe up
element.swipeUp()
// Swipe Down
element.swipeDown()
// Swipe Right
element.swipeRight()
// Swipe Left
element.swipeLeft()
|
Text Input
Type
1
2
3
4
| let textField = app.textFields["SearchBox"]
textField.tap()
textField.typeText("hello world")
|
Clear And Enter Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // File: XCTestExtensions > XCUIElementExtensions.swift
import XCTest
extension XCUIElement {
func clearAndEnterText(_ text: String) {
if value != nil {
guard let stringValue = value as? String else {
XCTFail("Tried to clear and enter text into a non string value")
return
}
tap()
let deleteString = stringValue.map { _ in XCUIKeyboardKey.delete.rawValue }.joined()
typeText(deleteString)
}
typeText(text)
}
}
|
Enter Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| extension XCUIElement {
// Workaround for keyboard issues when not visible
func enterText(text: String) {
tapElement()
if !self.waitForExistence(timeout: .small) {
tapElement()
}
let dismissKeyboardButton = XCUIKeyboardKey.enter
dismissKeyboardButton.waitForExistence(timeout: .medium)
clearAndEnterText(text)
if dismissKeyboardButton.isHittable {
dismissKeyboardButton.tapElement()
}
if elementType != .secureTextField {
waitForElementsValueToMatch(predicate: text)
}
}
}
|
Alerts
Dismissing Alerts
1
| app.alerts["Alert_Title"].buttons["ok"].tap()
|
Dismissing Action Sheets
1
| app.sheets["Sheet_Title"].buttons["ok"].tap()
|
System alerts within the application
1
2
3
4
5
6
7
| addUIInterruptionMonitor(withDescription: "Location Services") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
app.buttons["Request Location"].tap()
app.activate()
|
Sliders
1
| app.sliders.element.adjust(toNormalizedSliderPosition: 0.7)
|
validate slider value approximately
1
| XCTAssertEqual(app.sliders["playerScrubberView"].normalizedSliderPosition, 0.7, accuracy: 0.1)
|
Pickers
1
| app.pickerWheels.element.adjust(toPickerWheelValue: "Picker Wheel Item Title")
|
Date picker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| // File: PageObjects > DatePickerScreen.swift
import XCTest
class DatePickerScreen: Page {
private var dateField: XCUIElement { return app.textFields["Date"].firstMatch }
// MARK: Date format - day-month-year i.e 13-March-2015
func changePickerDate(_ date: String) {
let newDateFormat = date.components(separatedBy: "-")
let date = newDateFormat[0]
let month = newDateFormat[1]
let year = newDateFormat[2]
dateField.tap()
//Day
app.datePickers.pickerWheels.element(boundBy: 0).adjust(toPickerWheelValue: date)
// Month
app.datePickers.pickerWheels.element(boundBy: 1).adjust(toPickerWheelValue: month)
//Year
app.datePickers.pickerWheels.element(boundBy: 2).adjust(toPickerWheelValue: year)
// Dismiss dates picker
dateField.forceTap()
}
}
|
Web Links
1
| app.links["Tweet this"].tap()
|
Case Study
XCUItest allows you to set custom wait times for XCUIElements but having predefined timeouts comes in
handy, this can be accomplished by creating an enum that contain different timeouts then this can be used instead of hardcoded numbers.
1
2
3
4
5
6
7
8
| // File: Data > TimeOut.swift
/// Data file
enum Timeout: TimeInterval {
case extraSmall = 1
case small = 5
case medium = 10
case large = 20
}
|
You can add an function in XCUIElement extensions that takes the Timeout enum and outputs an Bool. You get the rawValue for Timeout and pass it to the native waitForExistence which returns whether the element exists.
1
2
3
4
5
6
7
8
9
10
| // File: XCTest > XCUIElement > XCUIElement.swift
import XCTest
extension XCUIElement {
@discardableResult
func waitForExistence(timeout: Timeout) -> Bool {
return waitForExistence(timeout: timeout.rawValue)
}
}
|
Many times functions returns a value, but sometimes you don’t care what the return value is – you might want to ignore it sometimes using @discardableResult.
In the PageObject class we need to create a private variable that has the element identifier, this makes it very convenient way of grouping XCUIElements by screens. The variable name should describe the element that it will referenced, also note XCUIElements should always be unique and try to add .firstMatch on to the end of an element in order for XCUITest to find the element quickly.
You can create a method that uses the variable to check if the element exists and the method then can be accessed by other classes, using this approach we remove code duplication but also it makes it easier to manage the UI tests.
1
2
3
4
5
6
7
8
9
10
11
12
| // File: PageObjects > AppPreviewScreen.swift
import XCTest
class AppPreviewScreen: Page {
private var okButton: XCUIElement { return app.buttons["ok"].firstMatch }
func isOkButtonExists() -> Bool {
okButton.waitForExistence(timeout: .small)
}
}
|
In the UI test we can create an instance of the page object class AppPreviewScreen then reference the function we require in order to validate the screen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // File: UITest > ExampleTest.swift
import XCTest
class ExampleTest: XCTestCase {
override func setUp() {
super.setUp()
continueAfterFailure = false
var appPreview: AppPreviewScreen = AppPreviewScreen(testCase: self)
}
override func tearDown() {
super.tearDown()
}
func testWaitForElement() {
XCTAssert(appPreview.isOkButtonExists())
}
}
|