Software Development

Automated Accessibility Testing Using Selenium

Accessibility testing is a crucial step in ensuring that web applications are usable by people with disabilities. Automated tools can help identify accessibility issues early in the development process. Selenium, primarily known for functional testing, can be combined with accessibility tools like axe-core, HTML CodeSniffer, or other libraries to perform accessibility testing. This article delves into automating accessibility testing using Selenium and the axe-core library.

1. Understanding Accessibility Testing Automation

Accessibility testing automation involves evaluating an application’s features to ensure that everyone can fully access and utilize it regardless of their abilities. This type of testing focuses on identifying potential barriers that may prevent differently-abled individuals from interacting with the application effectively. By automating these tests, developers can verify whether the application supports inclusivity, ensuring that specific actions can be performed without external assistance.

The core objective of accessibility testing is to create inclusive and usable software for people with varying abilities. For instance, accessibility measures address challenges visually impaired individuals face, such as ensuring compatibility with screen readers for navigating visual elements.

2. Why is Automated Accessibility Testing Important?

Automated accessibility testing plays a vital role in creating inclusive and user-friendly web applications. Here are the key reasons why it is essential:

  • Early Issue Detection
    Integrating automated accessibility tests into the development process ensures that accessibility issues are detected and resolved early. This reduces the cost and complexity of fixing these issues later in the project lifecycle.
  • Ensuring Compliance
    Automation helps teams ensure compliance with accessibility standards like WCAG (Web Content Accessibility Guidelines), ADA (Americans with Disabilities Act), or Section 508. Meeting these standards is crucial for avoiding legal risks and enhancing user satisfaction.
  • Enhancing User Experience
    By identifying and addressing barriers, automated testing ensures that all users, including those with disabilities, can interact with the application seamlessly. This improves the overall user experience and broadens the application’s audience.
  • Scalability and Efficiency
    Manual accessibility testing is time-consuming, especially for large or complex applications. Automation allows developers to quickly identify accessibility issues across multiple pages or components, saving significant time and effort.
  • Consistency in Testing
    Automated tools provide consistent and repeatable results, reducing the risk of human error. This is particularly important for maintaining high accessibility standards across multiple iterations of the application.

Automated accessibility testing is not a replacement for manual testing but a complementary approach to ensure faster, more reliable, and comprehensive accessibility checks.

3. Why Use Selenium for Accessibility Testing

Selenium is a popular browser automation framework, making it an excellent tool for simulating user interactions. When combined with accessibility libraries, Selenium can:

  • Navigate dynamically loaded content.
  • Simulate user actions to test interactive elements.
  • Provide detailed reports on accessibility violations.

3.1 Setting Up Selenium for Accessibility Testing

To begin, we will need to set up a Selenium environment. This involves installing dependencies and configuring the project. Here is a sample Maven pom.xml configuration:

    <dependencies>
        <!-- Selenium Java -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.27.0</version>
        </dependency>
        <!-- WebDriverManager for managing drivers -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>5.9.2</version>
        </dependency>
        <!-- JSON Parsing for axe-core results -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.18.2</version>
        </dependency>
    </dependencies>

Installing axe-core

To integrate accessibility testing, download the axe-core JavaScript and inject it into the web page during Selenium tests. We can obtain it from the axe-core GitHub repository.

4. Integrating axe-core with Selenium

Below is a basic example of how to inject the axe-core script into a web page using Selenium.

public class SeleniumAccessibilityTestAutomation {

    public static void main(String[] args) throws Exception {
        WebDriverManager.chromedriver().setup();
        WebDriver driver = new ChromeDriver();

        try {
            // Navigate to the target website
            driver.get("https://example.com");

            // Load axe-core script
            String axeScript = new String(Files.readAllBytes(Paths.get("path/to/axe.min.js")));

            // Inject axe-core into the page
            JavascriptExecutor js = (JavascriptExecutor) driver;
            js.executeScript(axeScript);

            // Run accessibility scan and store result in a Map
            Object axeResults = js.executeScript("return axe.run()");

            // Print results
            System.out.println(axeResults.toString());
        } finally {
            driver.quit();
        }
    }
}

This Java code automates accessibility testing for a webpage using Selenium and the axe-core library. It sets up the ChromeDriver with WebDriverManager, navigates to the target website, and loads the axe-core script from a local path. The script is injected into the page using Selenium’s JavascriptExecutor, and an accessibility scan is triggered with axe.run().

4.1 Writing Tests with axe-core

The following is an example of how to assert accessibility violations.

public class AccessibilityTestWithAssertions {

    public static void main(String[] args) throws Exception {
        WebDriverManager.chromedriver().setup();
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://example.com");

            // Load axe-core script
            String axeScript = new String(Files.readAllBytes(Paths.get("path/to/axe.min.js")));
            
            JavascriptExecutor js = (JavascriptExecutor) driver;
            js.executeScript(axeScript);

            // Run axe-core accessibility scan
            Object axeResults = js.executeScript("return axe.run()");

            if (axeResults instanceof Map) {
                // Parse JSON results
                ObjectMapper mapper = new ObjectMapper();
                String jsonResult = mapper.writeValueAsString(axeResults);
                JsonNode rootNode = mapper.readTree(jsonResult);

                // Analyze violations
                JsonNode violations = rootNode.path("violations");
                if (violations.size() > 0) {
                    System.out.println("Accessibility issues found:");
                    for (JsonNode violation : violations) {
                        System.out.println("Violation: " + violation.path("description").asText());
                        System.out.println("Impact: " + violation.path("impact").asText());
                        System.out.println("Nodes:");
                        violation.path("nodes").forEach(node
                                -> System.out.println(node.path("html").asText()));
                    }
                } else {
                    System.out.println("No accessibility violations found.");
                }
            }
        } finally {
            driver.quit();
        }
    }
}

Analyzing Accessibility Reports

The axe.run() function produces a JSON report detailing the results of the accessibility scan. Key fields in the report include violations, which lists all detected accessibility issues, along with their description, explaining the nature of each issue, and impact, indicating the severity level (critical, serious, moderate, or minor). Additionally, the nodes field specifies the elements on the webpage responsible for the issues.

When running the AccessibilityTestWithAssertions class, the output consists of structured JSON data (similar to the data below), which can be printed to the console for review.

Accessibility issues found:
Violation: Ensure buttons have discernible text
Impact: critical
Nodes:
<button class="ui-datepicker-trigger" type="button">
<!-- <img title="..." alt="..." src="/redesign/assets/demo-sites/mars/images/calendar.png"> -->
</button>
Violation: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
Impact: serious
Nodes:
<h3>Be Bold...</h3>
<p>Step out of your comfort zone, and into a rocket with enough fuel to blast a Manhattan-sized crater if it explodes. But it won't. Probably.<br>
  </p>
<h3>Countdown...</h3>
<p>If you're serious about traveling to Mars - really serious - then <a href="mars2.html?a=last_will">prepare your last will and testament</a>, and book a trip! </p>
<h3>Blast Off!</h3>
<p>Expect violent turbulence, bone-crushing g-forces, muscle atrophy, and certain death (hey, everyone's death is certain at some point, right?).<br>
  </p>
Violation: Ensure <iframe> and <frame> elements have an accessible name
Impact: serious
Nodes:
<iframe id="fafbba78" name="f2bc5e72d" scrolling="no" style="border: none; overflow: hidden; height: 62px; width: 292px;" class="fb_ltr" src="/assets/demo-sites/mars/js/likebox.html"></iframe>
Violation: Ensure every HTML document has a lang attribute
Impact: serious
Nodes:
<html class=" js no-flexbox flexbox-legacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths js no-flexbox flexbox-legacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths">
Violation: Ensure <img> elements have alternative text or a role of none or presentation
Impact: critical
Nodes:
<img src="/assets/demo-sites/mars/js/seg" width="1" height="1">
<img src="/assets/demo-sites/mars/images/mars-spaceman.jpg" class="" width="210" height="120">
<img src="/assets/demo-sites/mars/images/mars-spaceman.jpg" class="" width="210" height="120">
<img src="/assets/demo-sites/mars/images/mars-spaceman.jpg" class="" width="210" height="120">
Violation: Ensure the document has a main landmark
Impact: moderate
Nodes:
<html class=" js no-flexbox flexbox-legacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths js no-flexbox flexbox-legacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths">
Violation: Ensure landmarks are unique
Impact: moderate
Nodes:
<nav id="left-control-nav" class="pull-left">
Violation: Ensure links are distinguished from surrounding text in a way that does not rely on color
Impact: serious
Nodes:
<a href="mars2.html?a=last_will">prepare your last will and testament</a>
Violation: Ensure links have discernible text
Impact: serious
Nodes:
<a class="link" href="demo/mars/#"><i class="icon-menu-home"></i> </a>
<a href="mars2.html?a=crater_adventure">
<img src="/assets/demo-sites/mars/images/mars-spaceman.jpg" class="" width="210" height="120"></a>
<a href="mars2.html?a=crater_adventure">
<img src="/assets/demo-sites/mars/images/mars-spaceman.jpg" class="" width="210" height="120"></a>
<a href="mars2.html?a=crater_adventure">
<img src="/assets/demo-sites/mars/images/mars-spaceman.jpg" class="" width="210" height="120"></a>
<a href="mars2.html?a="></a>
<a target="player" data-text="Life was possible on Mars" class="fader first active" href="http://www.youtube.com/embed/OagLGti_hTE?controls=1&showinfo=1&modestbranding=0&wmode=opaque&enablejsapi=1" id="default"></a>
<a target="player" data-text="Why Mars died" class="fader first" href="http://www.youtube.com/embed/oC31pqk9sak?controls=1&showinfo=1&modestbranding=0&wmode=opaque&enablejsapi=1" id="default"></a>
<a target="player" data-text="The world that never was" class="fader first" href="http://www.youtube.com/embed/JgMXPXdqJn8?controls=1&showinfo=1&modestbranding=0&wmode=opaque&enablejsapi=1" id="default"></a>
Violation: Ensure all page content is contained by landmarks
Impact: moderate
Nodes:
<div style="width: 1px; height: 1px; display: inline;">
<div id="purposeDisclaimer">This web page is for demonstration purposes, to show common accessibility errors.</div>
<input type="text" class="search" name="query" placeholder="search">
<div class="span7 left-first pull-left" id="left-column">
<div id="widget-controls" class="widget-container head">
<h3>Book your Trip</h3>
<div id="route-type-radio-group" class="">
<span class="wrapper departure-city">
<span class="wrapper arrival-city">
<label for="deptDate0">Departure Date</label>
<input size="10" id="deptDate0" name="deptDate0" placeholder="mm/dd/yyyy" value="" tabindex="3" class="hasDatepicker input-dept">
<span class="wrapper time">
<span class="add-buttons wrapper add-trip add-leg-width">
<a class="add-leg"><span class="icon"></span>Add Another Trip</a>

</span>
<div id="pass-question-radio-group" class="">
<h3>Who Is Traveling?</h3>
<span class="wrapper">
<span class="traveler-label">Traveler</span>
</span>
<span class="wrapper age-range">
<select id="traveler0" class="traveler-type">
<option value="0">Adult (26+)</option>
<option value="1">Youth (12-25)</option>
<option value="2">Child (4-11)</option>
<option value="3">Senior (60+)</option>
</select>
</span>
<div class="add-buttons" id="add-traveler">
<h3 id="video-text">Life was possible on Mars</h3>
<li class="social-google">
<li class="social-app">
<li class="social-community">
<h4>Book Your Trip</h4>
<ul>
<h4>Mars Shuttles</h4>
<ul>
<h4>Mars Tourist Passes</h4>
<ul>
<h4>Mars Adventures</h4>
<ul>
<h4>FAQs</h4>
<ul>
<h4>Connect With Us</h4>
<ul>
<div id="copyright" class="container">
Violation: Ensure select element has an accessible name
Impact: critical
Nodes:
<select name="time0" id="time0" class="select-time" tabindex="4">
<select id="traveler0" class="traveler-type">
<option value="0">Adult (26+)</option>
<option value="1">Youth (12-25)</option>
<option value="2">Child (4-11)</option>
<option value="3">Senior (60+)</option>
</select>
Violation: Ensure tabindex attribute values are not greater than 0
Impact: serious
Nodes:
<input type="text" value="" class="city-input ac_input ui-autocomplete-input" autocomplete="off" id="from0" name="from0" tabindex="1" role="textbox" aria-autocomplete="list" aria-haspopup="true">
<input type="text" value="" class="city-input ac_input ui-autocomplete-input" autocomplete="off" id="to0" name="to0" tabindex="1" role="textbox" aria-autocomplete="list" aria-haspopup="true">
<input size="10" id="deptDate0" name="deptDate0" placeholder="mm/dd/yyyy" value="" tabindex="3" class="hasDatepicker input-dept">
<select name="time0" id="time0" class="select-time" tabindex="4">

5. Conclusion

In this article, we explored how to automate accessibility testing using Selenium and the axe-core library. We discussed the importance of accessibility testing, set up a Selenium project, integrated the axe-core script, and demonstrated how to perform scans to detect accessibility issues. By leveraging automation, we can efficiently identify and address barriers, ensuring web applications are inclusive and compliant with accessibility standards.

6. Download the Source Code

This article explored accessibility testing using Selenium.

Download
You can download the full source code of this example here: selenium accessibility testing

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button