Configuring UI tests with launch arguments
Helm Pro yearly subscribers now get a 30% discount on RocketSim thanks to contingent pricing on the App Store.
Xcode provides out of the box support for UI testing apps through the XCTest
framework. The anatomy of a unit test case consists of an instance of XCUIApplication
, which launches the target under test, and a set of assertions against such target.
The XCUIApplication
instance allows the test to access any element on the screen to perform actions such as taps, scrolls or even asserting that certain elements appear on screen:
final class UITesting: XCTestCase {
func test_WhenNavigateButtonIsPressed_ThenNavigationOccurs() throws {
// Create and launch the app under test
let app = XCUIApplication()
app.launch()
let navigateButton = app.buttons["Navigate"]
if navigateButton.waitForExistence(timeout: 10) {
navigateButton.tap()
}
let helloLabel = app.staticTexts["Hello"]
XCTAssertTrue(helloLabel.waitForExistence(timeout: 10))
}
In the preceding example, the test method launches the app, taps on a button of title “Navigate” if it exists and asserts that a label with the text “Hello” appears on screen.
While this example does the job in most cases, UI tests sometimes need to configure to the app under test. The test methods, if needed, can give the XCUIApplication
a set of launch arguments which can alter the behaviour of the app.
Passing launch arguments
The test method can pass launch arguments to an XCUIApplication
’s instance through the launchArguments
property:
func test_WhenNavigateButtonIsPressed_ThenNavigationOccurs() throws {
let app = XCUIApplication()
app.launchArguments = ["UITEST"]
app.launch()
// ...
}
This property then populates the arguments
value on ProcessInfo.processInfo
, which makes such arguments available to the app’s code.
Disabling animations
Turning off animations when running UI tests can help reduce their flakiness and improve their performance. The UIView.setAnimationsEnabled
method turns off all animations across the app.
The app should call this method as soon as it launches, solely when UI tests run, using the UITEST
launch argument defined in the previous section:
import SwiftUI
import UIKit
@main
struct TestApp: App {
init() {
if ProcessInfo.processInfo.arguments.contains("UITEST") {
UIView.setAnimationsEnabled(false)
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Localisation
Launch arguments can also come in handy to test an app’s localisation. The AppleLanguages
and AppleLocale
built in arguments set the test device’s language and region before the app is launched:
final class UITesting: XCTestCase {
func test_WhenNavigateButtonIsPressed_ThenNavigationOccurs() throws {
// Create and launch the app under test
let app = XCUIApplication()
app.launchArguments = [
"-AppleLanguages", "(es)",
"-AppleLocale", "es_ES"
]
app.launch()
// ...
}
}
Content size
XCUIApplication
provides a powerful set of tools to test an app’s accessibility compliance. For example, Rob Whitaker’s A11yUITests Swift Package makes excellent use of native UI testing tools to verify an app complies with common mobile accessibility rules and guidelines.
The built in UIPreferredContentSizeCategoryName
launch argument modifies the content size category for a XCUIApplication
instance. This parameter, in combination with snapshot testing, can help identify shortcomings in app’s accessibility support automatically.
final class UITesting: XCTestCase {
func test_WhenNavigateButtonIsPressed_ThenNavigationOccurs() throws {
// Create and launch the app under test
let app = XCUIApplication()
app.launchArguments = [
"-UIPreferredContentSizeCategoryName",
"\(UIContentSizeCategory.accessibilityExtraExtraExtraLarge.rawValue)"
]
app.launch()
// ...
}
}
The video below shows a set of UI tests running with different content size categories.