Testing your app's accessibility with UI Tests

Testing your app's accessibility with UI Tests

Discover how to perform an automated accessibility audit in Xcode with UI Tests.

We have seen in Testing your app's accessibility with the Accessibility Inspector how to run audits to identify possible accessibility issues within an app.
In addition to performing accessibility audits with the Accessibility Inspector, developers can automate the tests in the UI Tests with the performAccessibilityAudit(for:_:), introduced in Xcode 15.

In this article, we will explore how to run an accessibility audit using the XCTest framework by analyzing a simple app with some accessibility issues.

Create an accessibility test

When starting a new project, check the "Include Tests" option to enable testing in Xcode.

Xcode screenshot showing the creation of a new project with the Include Tests option checked
Including Tests in a new project

By doing this, Xcode will automatically generate some basic tests and add the testing target to your project.

Xcode screenshot showing the automatically generated UITests

Alternatively, you can add UI Tests to an existing project by adding a new target to the project.

Xcode screenshot showing the targets options
Xcode screenshot showing the addition of UI Tests
Add UI Tests in an existing project.

In our app, we have a simple view that showcases multiple accessibility errors.


import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Accessibility")
            .foregroundStyle(Color.white)
            .font(.system(size: 24))
            .background { Color.yellow }
    }
}

This view has the following issues:

  1. It doesn't support Dynamic Type since the text has fixed sizes
  2. The contrast between the text and its background is not sufficient

To analyze the view, define a new method testAccessibility() in the project's UITests file.

func testAccessibility() throws {
    let app = XCUIApplication()
    app.launch()
    try app.performAccessibilityAudit()
}       

With this test we create an instance of the app then, after launching it, we start performing an accessibility audit.

The performAccessibilityAudit() function will highlight similar issues to those found in an accessibility audit with the Accessibility Inspector App.

You can run the test by either clicking on the button with the gray play icon located near the methods of the class, near the class itself or in the Xcode Test navigator, where you can have an overview of all the tests in your project and run specific tests individually. Additionally, you can use the ⌘+U shortcut to run all tests.

Gif showing how to run all tests and the failure of the testAccessibility test
Running the tests

The test fails immediately, stopping showing the first cause of the failure however, in our example for sure there are two.
By default, UI tests stop their execution immediately when a failure occurs. You can ensure that the test doesn't stop after finding the first error by setting the continueAfterFailure property to true in the setUpWithError(). This will allow you to discover all the potential accessibility issues in the view.


override func setUpWithError() throws {
    // Put setup code here. This method is called before the invocation of each test method in the class.
    
    // In UI tests it is usually best to stop immediately when a failure occurs.
    continueAfterFailure = true
    
    // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}

Gif showing how to run all tests and the failure of the testAccessibility test with 2 failures
Running the tests continuing after a failure occur

As expected, the test failed with:

  • Contrast nearly passed
  • Dynamic Type font sizes are unsupported

Analyze errors

We can delve deeper into analyzing these errors by pinpointing the specific portion of the view responsible for each failure. In the Report navigator section, navigating to the test section and double-clicking on the error allows us to precisely identify the element causing the failure.

Gif showing how to check the element that is causing the failure
Report navigator - Element that is causing the failure

Test specific accessibility properties

By defaults the performAccessibilityAudit(for:_:) will test all the various accessibility properties. We can customize the audit test to focus on specific accessibility aspects that need to be checked by providing an XCUIAccessibilityAuditType the to the method.

func testDynamicType() throws {
    let app = XCUIApplication()
    app.launch()
    try app.performAccessibilityAudit(for: .dynamicType)
}    

In this test, we pass a set of audit types as input, configuring what the audit will specifically test for. For a comprehensive list of audit types, you can check the documentation.

Alternatively, we can also exclude specific audit types.

func testAccessibilityExcludingElementDescription() throws {
    let app = XCUIApplication()
    app.launch()
    try app.performAccessibilityAudit(for: .all.subtracting(.sufficientElementDescription))
}

In certain cases, there might be issues that need to be ignored by the test. We can accomplish this by passing a closure that is called for each issue found by the audit, allowing us to choose which issues need to be ignored.

func testAccessibility() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()
    
    try app.performAccessibilityAudit(for: [.dynamicType, .contrast, .sufficientElementDescription]) { issue in
        var shouldIgnore = false
        
        if let element = issue.element,
           issue.auditType == .contrast {
            shouldIgnore = true
        }
        return shouldIgnore
    }
}

Conclusion

UI tests streamline the process of making your app accessible for everyone, highlighting the issues that need to be corrected.

Anyway, it’s important to remember that audits are limited to the elements on the screen, and to really make sure your app is easy to use for everyone audits must be paired with user testing, testing how usable the app is with assistive technologies such as VoiceOver and applying accessibility features such as Larger Text Sizes, Increase contrast and others.