Configuring UI tests with launch arguments

Sponsored

Helm logo
Helm for App Store Connect

A native app for App Store Connect to make managing your apps faster, simpler and more fun. Get early access now!

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:

NavigationUITests.swift
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:

NavigationUITests.swift
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:

TestApp.swift
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:

NavigationUITests.swift
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.

NavigationUITests.swift
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.