How do you test accessibility: the Accessibility Inspector, automated audits in XCUITest, identifiers versus labels, and manual testing?
Predict first
Does a UI test that runs an accessibility audit (all, and a filtered one with an issue handler) and checks an element’s label and identifier compile for iOS?
import XCTest
final class AccessibilityAuditTests: XCTestCase {
@MainActor func testScreenPassesAudit() throws {
let app = XCUIApplication()
app.launch()
try app.performAccessibilityAudit()
try app.performAccessibilityAudit(for: [.contrast, .dynamicType, .hitRegion]) { issue in
issue.element?.label == "Decorative"
}
let share = app.buttons["share"]
XCTAssertEqual(share.label, "Share")
XCTAssertEqual(share.identifier, "share")
}
}
- Yes
- No, audits are only available in Xcode’s inspector
Mental model. An audit tool is a spell checker: it finds the typos. It cannot tell you whether the essay makes sense, which needs a person reading it.
Test at three levels. Inspect: Xcode’s Accessibility Inspector (Xcode ▸ Open Developer Tool) points at any element in the simulator or on a connected device and shows its label, value, traits, hint and frame, runs an audit that flags missing labels, small targets, low contrast and clipped text, and can simulate display settings such as larger text. Automate: in a UI test try app.performAccessibilityAudit() runs the same kinds of checks on the current screen and fails the test on an issue, and you can limit it to categories (.contrast, .dynamicType, .hitRegion, .sufficientElementDescription) and handle known exceptions in a closure, as in the check below. Add one audit per key screen to the UI tests you already run (Q449) so regressions fail the build; you can also assert labels and values in tests (button.label). Automation catches the mechanical problems, and manual testing is still required because a tool cannot tell whether a label makes sense.
Be exact about identifiers versus labels. An accessibilityIdentifier is for tests and is not spoken or shown to users, while the accessibilityLabel is what VoiceOver reads, so UI tests can find an element by a stable identifier while assistive technology reads a meaningful, localised label, and the two should not be confused or merged. Test by hand on a device with VoiceOver on (complete a core task with the screen curtain on), with the largest accessibility text size, Voice Control and Switch Control, Reduce Motion, Increase Contrast, in light and dark and in RTL, with a hardware keyboard, and ideally with people who rely on these tools every day. Record findings with the severity and the user impact, fix them as ordinary bugs, and re-test with the same method. Do not rely on a single audit tool score: green means the common mechanical rules pass, not that the app is usable.
XCUITest exposes the audit as an API, so it can run in CI with the rest of the UI tests.
Common traps
- Treating a passing audit as proof the screen is accessible. It catches mechanical issues; labels can be present and still meaningless.
- Using accessibility identifiers as labels (or reverse). Identifiers are not spoken; labels are, and should be human and localised.
They might follow up with
- What can an audit not tell you? Whether labels are meaningful, whether the order makes sense and whether the task is completable.
- Where do audits fit in CI? One per key screen inside the UI test suite, failing the build on new issues.
Related questions
- 24.5.2 How do you ship accessibility as a bar, not a backlog: a definition of done, accessible-by-default…
- 24.1.1 How does VoiceOver read your UI: accessibility elements, labels, values, hints and traits, and how do you fix…
- 24.2.1 How does Dynamic Type work, how do you support it in UIKit and SwiftUI, and how do layouts adapt at…
- 22.5.2 How do you write UI tests that stay stable: accessibility identifiers, page objects, launch arguments…
How do you ship accessibility as a bar, not a backlog: a definition… →