XCUITest · UI location automation
Make XCUITest location simulation prove a user outcome
XCUITest location simulation gives a UI test process a controlled proxy location through XCUIDevice and XCUILocation. The useful test is not the assignment itself. It is the complete contract around it: a known app state, location set at the right time, an observable loading boundary, an accessible result, and teardown that prevents the next test from inheriting the input.
How should an XCUITest set an iPhone location?
Create an XCUILocation from a known CLLocation, assign it to XCUIDevice.shared.location before launching the app or feature that reads location, and wait for a product-specific completion state. Assert both a fresh location-dependent result and the visible workflow. At teardown, clear the proxy location, reset test data, and verify that tests do not depend on execution order.
Understand the test-bundle and app-process boundary
Apple distinguishes a simulated location configured for code in a test bundle from the location needed by an application launched as a separate process during UI automation. For the latter, the UI test sets XCUIDevice.shared.location to an XCUILocation. This process boundary explains a common failure: a developer selects a test-plan location, sees the test bundle use it, and assumes the launched app received the same input.
Put the location assignment in the UI-test fixture that owns the scenario. Then launch or navigate in a sequence that makes the product's read point unambiguous. If the app fetches a location only during startup, setting the value after launch is too late. If the feature requests a new fix when a screen appears, document that behavior and wait for its application state rather than relying on an arbitrary delay.
- Use constructed values inside unit tests that call location logic directly.
- Use XCUIDevice location for a separately launched app under UI automation.
- Set the proxy before the code path that requests the relevant location.
- Keep the assignment near the scenario so ownership and cleanup are visible.
Give every UI test an independent fixture lifecycle
A reliable test must pass when run alone, after another test, or in a different order. Define the coordinate, permission state, account, backend fixture, saved place, and launch arguments as one case. Avoid a shared mutable city that one test changes for another. When several tests use the same public coordinate, a helper may construct it, but each test still owns its setup and teardown.
Clear the XCUILocation proxy when the scenario ends so the device can return to the location Core Location otherwise provides. Also remove app-side selections and test-backend overrides. A nil proxy alone cannot erase a cached store, route, or account market. Prove independence by rerunning a neutral-location case after the most stateful test and by randomizing order in a non-release test plan.
- Name fixtures for behavior, such as outsideServiceArea, not only for cities.
- Prefer public test points and avoid customer or employee addresses.
- Reset permissions through an approved test mechanism when the permission branch matters.
- Do not let one test rely on a location or login created by another.
Control permissions, cache, and competing region signals
A mock location value does not grant permission. The app can remain denied, restricted, or limited to reduced accuracy, and the UI must handle each state honestly. Build separate cases for the authorization decisions your product supports. Do not dismiss the system prompt by coordinate tapping in every test; where possible, move authorization handling behind launch-state preparation and reserve end-to-end prompt coverage for a focused suite.
Location is also not the same as IP region, system locale, App Store storefront, saved address, or account country. If a test expects regional content, state which signal owns the decision. A backend request should carry a run identifier or test fixture key so the test can distinguish a valid response from a cached production-like result. This is especially important when a location change triggers asynchronous network work.
- Record authorization and accuracy authorization with the expected screen.
- Make manual-city precedence an explicit product rule.
- Separate device coordinate from IP and account market in assertions.
- Use a test backend or isolated tenant for destructive or stateful flows.
Wait on accessible product state instead of timing guesses
UI automation becomes flaky when a test sleeps and then reads whichever screen happened to finish. Expose an accessibility identifier and meaningful value for the state users rely on: selected branch, availability message, route phase, or permission fallback. Wait for that element or state transition with a bounded timeout, and fail with diagnostics that identify the last accepted timestamp and expected fixture.
Avoid making precise coordinates part of ordinary accessibility labels if users do not need them. A test-only diagnostic surface, launch-gated debug panel, or structured attachment can preserve the low-level evidence without degrading privacy or the production experience. Assert content and control behavior as a user would encounter it, including VoiceOver-friendly labels for any status that is visible in the shipping app.
- Wait for a named state change, not a fixed sleep.
- Assert the final task the user can complete, not only an internal coordinate label.
- Capture a screenshot when visual state helps explain a failure.
- Attach structured diagnostics when cache, backend, or timestamp can be ambiguous.
Keep CI evidence useful and data-minimal
Apple's XCTest attachments can preserve screenshots, strings, files, and other output in test results. Retain what helps reproduce a defect: fixture name, public coordinate or region identifier, authorization state, accepted timestamp, build, backend fixture version, and the failed assertion. Avoid attaching full production responses, personal routes, credentials, or unrelated device logs.
A GPS changer marketing label is not a CI acceptance criterion. The automated contract should remain valid if the input mechanism changes: given an authorized developer location and known environment, the owned app presents the expected state, records the expected test event, and recovers afterward. WLOC can organize coordinate inputs and device-run evidence outside XCUITest, but source-controlled UI automation should keep its deterministic fixture and assertions in the project.
- Set attachment retention deliberately instead of saving everything forever.
- Redact secrets and avoid personal coordinates in CI artifacts.
- Store build and backend fixture versions with the result.
- Review flaky retries as failures of evidence, not proof of eventual correctness.
Diagnose an XCUITest location failure by layer
The same unchanged screen can have very different causes, so capture the narrowest evidence that distinguishes them.
| Observed failure | Likely layer | Next assertion |
|---|---|---|
| App uses the physical place | Proxy set after the app's read point | Set before launch and expose accepted timestamp |
| Permission screen blocks progress | Authorization fixture not controlled | Assert status, usage flow, and chosen fallback |
| Old city remains visible | App or account cache | Clear saved selection and verify response run ID |
| Map moves but catalog does not | Backend or competing region signal | Record coordinate, IP, account market, and decision source |
| Test passes only after another test | Shared state or missing teardown | Run alone, clear proxy, and reset fixture ownership |
Create a location-aware UI test that survives isolation
Use one scenario contract from setup through evidence retention so the test explains both failure and cleanup.
Choose the user outcome
Name the screen, control, or workflow that must differ at the authorized test coordinate.
Declare every input
Set the XCUILocation, permission state, account, saved place, backend fixture, and launch arguments for this test.
Assign before the read
Set XCUIDevice.shared.location before app launch or before the documented feature request point.
Wait for application state
Use accessibility identifiers and bounded expectations to observe the completed product decision.
Attach narrow evidence
Preserve the fixture name, status, timestamp, result, and helpful screenshot without collecting unrelated personal data.
Clear and rerun neutral
Remove the proxy and dependent state, then verify an independent neutral case succeeds without test-order assumptions.
Frequently asked questions
Why does a test-plan location not reach my UI-tested app?
The reason is architectural: the app under UI automation runs as a separate process. Set XCUIDevice.shared.location to an XCUILocation for that UI test rather than assuming the test bundle's simulated value crosses the process boundary.
When should I set XCUILocation?
Set it before the app or feature requests location requests the location you intend to test. For startup behavior, assign it before launch. Document a later request point if the app refreshes on demand.
Does XCUILocation bypass an iPhone permission decision?
Authorization and accuracy are independent inputs. The UI test should cover allowed, denied, restricted, and product-supported fallback states separately.
What should a failed location UI test attach?
Attach the fixture name, public test point or region ID, authorization, accepted timestamp, app and backend versions, final assertion, and a useful screenshot. Exclude secrets and personal route data.
Sources
- Apple testing sourceSimulating location in testsThe distinction between test-bundle simulation and XCUILocation for a separately running UI-tested app.
- Apple API referenceXCUIDevice locationHow a UI test assigns a proxy location and returns to the location Core Location otherwise provides.
- Apple API referenceXCUILocationThe proxy type that represents simulated latitude, longitude, and course information.
- Apple XCTest sourceAdding attachments to testsHow XCTest retains screenshots, text, files, and other test output for analysis.