I’m writing a customized progress view in UIKit:
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 habits as intently as attainable. One essential side of that is accessibility: ideally, my progress view ought to be indistinguishable from a system progress view to VoiceOver customers.
To this finish, I’d like to put in writing 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 must always test what UIProgressView
’s accessibility properties truly are to verify we see the right knowledge:
/// Easy checks 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
XCTAssertTrue(progressView.isAccessibilityElement)
XCTAssertEqual(progressView.accessibilityLabel, "Progress")
XCTAssertEqual(progressView.accessibilityTraits, [.updatesFrequently])
XCTAssertEqual(progressView.accessibilityValue, "40%")
// Extra assertions ...
}
}
Relying on the setting wherein 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 software, solely the primary assertion (
isAccessibilityElement
) fails, the others cross.
(And sure, I do know among the assertions are locale-dependent. In case your check app has localizations for different languages than English, you will have to configure the right language in your construct scheme when operating the check.)
Dominik Hauser discovered that we are able to make the primary assertion cross 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, top: 20)))
progressView.progress = 0.4
let window = UIWindow(body: CGRect(x: 0, y: 0, width: 400, top: 400))
window.makeKeyAndVisible()
window.addSubview(progressView)
XCTAssertTrue(progressView.isAccessibilityElement)
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:
-
Your check goal have to be connected to an app goal. The checks 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 checks aren’t injected right into a operating app.
-
Set a non-zero body on the view below check.
-
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. Namewindow.makeKeyAndVisible
.
I say “appear to be” as a result of I’m not sure the habits is 100% deterministic. Some views, like UILabel
, don’t require the extra setup.
Extra importantly, I’ve seen my checks fail a minimum of 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 checks. It was as if your complete accessibility system on the simulator was not operating. Attaching the Accessibility Inspector to the simulator as soon as appeared to repair the difficulty (possibly this precipitated the accessibility system to restart), and I haven’t been in a position to reproduce the issue since, even after a number of reboots. The whole lot seems to be working now.
My check setting: Xcode 11.4.1 with the iOS 13 simulator.