UITestHelpers
A collection of useful helper functions to make writing UI tests at least a little bit less painful.
Installation
CocoaPods
You can install UITestHelpers by adding them to your UI tests section in your Podfile:
platform :ios, '10.0'
use_frameworks!
target '<name-of-your-UI-tests-target>' do
pod 'UITestHelpers'
end
Then run pod install
.
Manually
Of course you can just drag the source files into your project, but using CocoaPods is really the recommended way of using UITestHelpers.
Usage / Examples
class MyUITests: XCUITestCase {
override func setUp() {
super.setUp()
self.continueAfterFailure = false
self.addAlertsHandler(for: ["Allow", "OK"])
self.app.launchEnvironment = ["AutoCorrection": "Disabled"]
self.app.launch()
}
func testSomething() {
// tap an element (it's important to wait for an element to exist, before tapping it)
element.waitAndTap()
// tap a button
self.tapButton("buttonAccessibilityIdentifier")
// tap a cell in a collectionView (scroll to the right, if needed)
self.tapCollectionViewCell("collectionViewCellAccessibilityIdentifier", in: "collectionViewAccessibilityIdentifier", scrollDirection: .right(100))
// handle (dismiss) permission alerts
self.addAlertsHandler(for: ["Allow", "OK"])
// tap the "Continue" button on the next alert dialog
self.tapAlertButton(name: "Continue")
// show keyboard
self.app.showKeyboard(for: self)
// really type on the keyboard (sometimes this is needed in contrast to `XCUIElement.typeText`)
self.app.typeOnKeyboard(text: "1337")
// hide keyboard
self.app.hideKeyboard()
}
}
Reliability
accessibilityIdentifier
s
Using To get reliable results from your tests, it's recommended, to use accessibilityIdentifier
s, wherever possible.
For static stuff you can just set them right in Interface Builder:
Or if the element does not have an Accessibility
section, using User Defined Runtime Attributes
:
For dynamic stuff however, you'll have to set it in code. Some may consider this a kind of clutter to have test-only stuff in the code base, but as it's really just a one-liner in e.g. a UITableViewCell
. And I would really take "this clutter" anytime over less reliable UI tests (e.g. when using indices instead).
E.g.:
let dynamic = "somethingSpecificToThisCell"
self.accessibilityIdentifier = "your\(dynamic)IdentifierHere"
How to ensure a clean app state for each test
As Apple doesn't provide a way to really clean the app state for each test, we'll have to do it manually by providing our own "main function".
For that we'll use a launch argument like --Reset
that we'll pass to our XCUIApplication
in our tests setup (see XCUITestCase
base class).
- Remove the
@UIApplicationMain
annotation from your AppDelegate. - Create a
main.swift
file in your project with following content:
_ = autoreleasepool {
if ProcessInfo().arguments.contains("--Reset") {
// Delete files, whatever...
}
UIApplicationMain(
CommandLine.argc,
UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to:
UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)),
nil,
NSStringFromClass(AppDelegate.self)
)
}
See also Resetting iOS Simulator for UI tests.
Trouble shooting
The software keyboard is not showing up
Xcode 10+
func disableHardwareKeyboard() {
let setHardwareLayout = NSSelectorFromString("setHardwareLayout:")
UITextInputMode.activeInputModes
.filter({ $0.responds(to: setHardwareLayout) })
.forEach { $0.perform(setHardwareLayout, with: nil) }
}
Xcode 9-
Make sure the hardware keyboard is disconnected.
- Pressing
cmd
+shift
+k
in the simulator will toggle the hardware keyboard - Even better: Disable it in a
Build Phase
script, in your UI tests target, like follows, to ensure this for each test run:
defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool NO
Debugging
Debug View Hierarchy
feature
Xcode's Here you can see all the accessibility information for an element.
Accessibility Inspector
Use this nice app (inclued in Xcode's Developer Tools) to inspect the app in a simulator, and get accessibility information as well as the hierarchy of the element.