Testing UIKit accessibility in unit exams – Ole Begemann


I’m writing a customized progress view in UIKit:

My round progress view

One of many design objectives for the view is to be a drop-in substitute for UIProgressView. This implies it ought to mannequin UIProgressView’s API and conduct as carefully as doable. One necessary facet of that is accessibility: ideally, my progress view needs to be indistinguishable from a system progress view to VoiceOver customers.

To this finish, I’d like to write down a unit check that fails when it finds a discrepancy between my view’s accessibility settings and people of an ordinary UIProgressView. Earlier than we write that check, although, we should always examine what UIProgressView’s accessibility properties truly are to ensure we see the proper information:

/// Easy exams for the accessibility properties of UIKit controls,
/// to show that the check setup is working.
class UIAccessibilityTests: XCTestCase {
  func testUIProgressViewAccessibility() {
    let progressView = UIProgressView()
    progressView.progress = 0.4

    XCTAssertEqual(progressView.accessibilityLabel, "Progress")
    XCTAssertEqual(progressView.accessibilityTraits, [.updatesFrequently])
    XCTAssertEqual(progressView.accessibilityValue, "40%")
    // Extra assertions ...

Relying on the atmosphere through which this check is run, some or all of those assertions unexpectedly fail:

  • In a unit check goal for a framework, all assertions fail.
  • In a unit check goal for an iOS utility, solely the primary assertion (isAccessibilityElement) fails, the others go.

(And sure, I do know among the assertions are locale-dependent. In case your check app has localizations for different languages than English, you’ll have to configure the proper language in your construct scheme when operating the check.)

Dominik Hauser came upon that we are able to make the primary assertion go by including the view to a visual window and giving it a non-zero body:

  func testUIProgressViewAccessibility() {
    let progressView = UIProgressView(body: (CGRect(x: 0, y: 0, width: 200, peak: 20)))
    progressView.progress = 0.4

    let window = UIWindow(body: CGRect(x: 0, y: 0, width: 400, peak: 400))

    XCTAssertEqual(progressView.accessibilityLabel, "Progress")
    /// ...

Thanks quite a bit for the assistance, Dominik (and everybody else who replied to my query)!

To summarize, the necessities for testing UIKit’s accessibility properties in a unit check appear to be:

  1. Your check goal should be connected to an app goal. The exams don’t work in a unit check goal for a library/framework, presumably as a result of making a window seen has no impact when the exams aren’t injected right into a operating app.

  2. Set a non-zero body on the view beneath check.

  3. Create a UIWindow (ideally additionally with a non-zero body, though this wasn’t related in my testing) and add the view to the window. Name window.makeKeyAndVisible.

I say “appear to be” as a result of I’m not sure the conduct is 100% deterministic. Some views, like UILabel, don’t require the extra setup.

Extra importantly, I’ve seen my exams fail no less than as soon as. When that occurred, properties like accessibilityLabel didn’t even return their appropriate values when logged to the console from the check app, not simply within the exams. It was as if the whole accessibility system on the simulator was not operating. Attaching the Accessibility Inspector to the simulator as soon as appeared to repair the problem (perhaps this brought about the accessibility system to restart), and I haven’t been capable of reproduce the issue since, even after a number of reboots. Every part seems to be working now.

My check atmosphere: Xcode 11.4.1 with the iOS 13 simulator.