cswork / libraryOpen practice workspace

Articles

XCUITest Interview Questions: Element Queries, Launch State, and System Alerts

Practice XCUITest interview questions with a launch-state fixture, identifier queries, exists-versus-hittable checks, and alert handling.

An XCUITest interview answer is strongest when it separates three questions: what state did the app launch into, which accessibility element does the query identify, and what currently blocks interaction? An element can exist yet be offscreen or covered; a system permission alert may belong outside the app; a test that starts from yesterday’s login state may never reach the intended screen. No amount of waiting repairs an ambiguous query or uncontrolled launch state.

Official facts here come from Apple’s XCTest and XCUITest documentation. The sample onboarding flow is an editorial fixture; its launch arguments and accessibility identifiers are examples an app team would implement, not built-in Apple switches. This is not a report of a candidate’s exact interview prompt.

Start with a reproducible launch contract

Apple’s XCUIApplication documentation describes a proxy used to launch, monitor, and terminate an application under UI test. It exposes launchArguments and launchEnvironment. The testing team can use these to request a deterministic app state, but the application itself must interpret the values. Passing a string such as "-resetOnboarding" does nothing unless app startup code recognizes it.

For this hypothetical onboarding fixture, the app supports a test-only launch argument that clears onboarding completion and a launch environment value that selects a local fixture account. The test sets both before app.launch(), then asserts that the welcome screen appears. A second test requests a returning-user state and expects the home screen. The two tests should not depend on their execution order.

A good answer specifies the cleanup boundary. Clearing only UserDefaults may leave a keychain token or backend record that restores the prior session. Launch-state control may require a test account, a local stub, or an app-provided reset hook. The test should verify the first screen rather than assuming the launch flag worked. If a reset would touch production data or shared accounts, use a dedicated test environment and scoped fixture identities.

What does an element query actually select?

An XCUIElementQuery is a search over the accessibility hierarchy. Apple’s query API supports element types, descendants, matching, and identifiers. In the fixture, the app exposes accessibilityIdentifier values such as onboarding.continue and onboarding.email. The test can query app.buttons["onboarding.continue"] and app.textFields["onboarding.email"] rather than searching all visible text on screen.

Identifiers are a testability contract owned by the app team. A label is often user-facing and can change with localization, copy edits, or dynamic data. An identifier is more stable when chosen intentionally, though it can still be duplicated. If two cells each contain a button with identifier "open", querying the entire app for that identifier is ambiguous. Scope the query to the cell representing the intended item, or assign a stable per-item identifier where appropriate.

Do not use a broad element query merely because it returns one element on the current simulator. Ask whether the selector remains unique when the user has two notifications, the list is reordered, the app is localized, or a modal is present. Conversely, a selector built from an internal implementation detail may be brittle after a harmless layout refactor. The best query expresses the user-visible target with enough accessibility structure to remain unique.

Existence is not hittability

Apple’s isHittable reference says an element can exist but not be tappable at its current location. It may be offscreen, obscured, or otherwise without a usable hit point. This gives a precise answer to the common interview puzzle: app.buttons["Continue"].exists can be true while a tap still fails. Existence answers whether the query finds an element in the hierarchy; hittability addresses current interaction.

Consider a long onboarding form. The Continue button is present below the fold. The test should not repeatedly tap it or increase a timeout while leaving it offscreen. Find the correct scroll container, scroll in a bounded way, then confirm the button is hittable before tapping. If a keyboard covers it, dismiss or move the keyboard through a user-realistic action. If an overlay blocks it, assert and resolve the overlay rather than scrolling indefinitely.

The distinction also prevents a weaker assertion: waitForExistence(timeout:) returning true does not prove the user can interact with the element. Apple documents waitForExistence for waiting on presence. Use it when the UI element is expected to appear asynchronously, and separately test visibility or hittability when interaction is the contract.

A three-state query exercise

Use the following editorial state table for the same onboarding button. No simulator was run for this article; the table is a reasoning aid checked against Apple’s API definitions.

Screen stateQuery exists?Hittable now?Appropriate next step
Welcome screen has not rendered buttonNoNoWait for expected view or diagnose launch.
Button exists below scroll viewportYesNoScroll the correct container, then recheck.
Button visible but system alert covers appYesUsually noHandle the alert, then retry the intended action.
Button visible and unobstructedYesYesTap and assert the resulting state.

Do not overstate the third row as a universal platform guarantee; exact hit testing and alert timing can vary. Its purpose is to force a separate observation of the interruption. A brittle test treats every failed tap as “not found.” A better one records the current accessibility hierarchy, the queried element, its existence, its hittability, and any covering UI. Those observations lead to different repairs.

Suppose the test repeatedly times out on the Welcome screen. Ask whether the app actually launched into the test state, whether a prior session took it to Home, whether the identifier exists in the shipped build, and whether a system alert interrupted launch. A selector rewrite is premature until the initial state is known.

How should system alerts be handled?

A permission dialog is not an ordinary child of the app’s own screen. Apple’s UI interruption guidance describes adding a UI interruption monitor with a handler that recognizes and handles the interrupting UI. An interruption monitor is for a specific expected alert, not a blanket “tap Allow” rule that can conceal an unexpected permission request. The handler should identify the alert and intended button, then return whether it handled the interruption.

The monitor is exercised when UI testing encounters an interruption during an interaction. Therefore, registering a handler and immediately asserting that the dialog disappeared may be insufficient; perform the next relevant UI interaction and check the resulting state. Test the allow and deny paths separately if both matter. A test that always grants permission can miss the app’s denial recovery behavior.

Permission state itself can persist outside the application’s in-process memory. If one test grants notification access, a later test may not see the first-run prompt. Use a controlled simulator/device strategy or explicit reset mechanism supported by the test environment, and record the precondition. Never make the assertion “the alert appears on every launch” unless the fixture truly resets that permission state.

A compact Swift-style test sketch

A conceptual setup can be written without pretending this code compiled in the current workspace:

let app = XCUIApplication()
app.launchArguments += ["-resetOnboarding"]
app.launchEnvironment["TEST_ACCOUNT"] = "fixture-user"
app.launch()
let continueButton = app.buttons["onboarding.continue"]
XCTAssertTrue(continueButton.waitForExistence(timeout: 5))
// Scroll or resolve an interruption if it is not hittable.
XCTAssertTrue(continueButton.isHittable)
continueButton.tap()

The app must implement the launch hooks and identifiers. The five-second timeout is an example test budget, not an Apple recommendation or a promise that all CI simulators settle within five seconds. A real test should assert the next screen after tap and capture useful diagnostics on failure. Avoid a fixed sleep followed by a tap; it obscures whether the problem is launch, query, UI readiness, or obstruction.

For system alerts, install a narrowly named interruption monitor before the action that triggers the permission. Verify that it sees the expected alert and chooses the intended response. Then perform an interaction that lets XCTest handle the interruption, and assert the app’s resulting state. If the test needs to prove the app’s own permission rationale screen, distinguish that app-owned view from the operating-system dialog.

What makes the fixture independent under parallel testing?

UI tests often share simulator state, app data, backend accounts, and permission choices. Two tests running concurrently against the same simulator and account can interfere even if each test method creates a fresh XCUIApplication object. Isolation requires separate destinations or a runner configuration that prevents unsafe overlap. State setup should be idempotent: asking for the signed-out onboarding state twice should yield the same start screen.

Use distinct fixture accounts or separate simulator instances for tests that write server state. Avoid a global “delete all test users” teardown that can erase another test’s data. Capture the device OS version, app build, locale, initial screen, permission state, and failing query when a test flakes. These facts are more useful than retry count alone.

If the app has both UIKit and SwiftUI surfaces, test against the resulting accessibility tree rather than assumptions about the view implementation. An accessibility identifier is only useful if it is exposed on the actual element queried. A query that fails only after a component rewrite should trigger an accessibility hierarchy inspection and a discussion with the app team, not a switch to fragile coordinate taps.

Fast interview answers

Why prefer identifiers over labels? Identifiers can form a stable test contract; labels are often localized user copy. Still ensure identifiers are unique in the relevant scope.

Does waitForExistence make a button tappable? No. It waits for presence. Check current hittability and resolve scroll, keyboard, or covering UI as needed.

Do launchArguments reset the app automatically? No. They are input to app startup; the app or test harness must implement the reset behavior.

Why did a permission alert appear only once? The operating system may persist permission state. Control that precondition and test both allow and deny paths where relevant.

What do you inspect after an intermittent failed tap? Initial app state, query match count, hierarchy, exists, isHittable, active interruption, device details, and the next-screen assertion.

Sources and further reading