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.
You can download the full source code of this example here: selenium accessibility testing