Skip to content

Automated Accessibility Testing — Complete Guide

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Automated Accessibility Testing. We cover key concepts, practical examples, and best practices to help you master this topic.

Automated accessibility testing uses tools like axe-core, Pa11y, and Lighthouse to programmatically detect WCAG violations, integrate with CI/CD pipelines, and prevent accessibility regressions before code reaches production.

What You'll Learn

  • Setting up automated accessibility tests
  • Integrating with CI/CD pipelines
  • Configuring rule sets and thresholds
  • Handling known issues and false positives
  • Reporting and tracking violations over time

Why It Matters

  • Automated tests catch regressions instantly
  • Testing early reduces fix costs
  • CI/CD integration ensures every PR is checked
  • Automated tests establish a baseline and track progress

Real-World Use

  • A financial services company runs axe-core on every PR
  • An e-commerce site uses Lighthouse CI for performance and accessibility
  • A government portal requires 100 percent accessibility pass on deployment
  • A design system enforces accessibility with automated component tests
flowchart LR
  A[Developer Push] --> B[CI Pipeline]
  B --> C[Build Project]
  C --> D[Run axe-core Tests]
  D --> E{Pass?}
  E -->|Yes| F[Deploy]
  E -->|No| G[Block PR]
  G --> H[Developer Fixes]
  H --> C

Setting Up Automated Testing

Automated accessibility testing integrates into your existing testing framework. The most common approach is running axe-core in your test suite.

Code Example: Jest with axe-core

// jest.config.js
module.exports = {
    testMatch: ['**/*.a11y.test.js']
};

// setup.js
const { configureAxe } = require('jest-axe');

const axe = configureAxe({
    rules: {
        // Skip color contrast in CI (test separately)
        'color-contrast': { enabled: false },
        // Skip region rule for known patterns
        'region': { enabled: false }
    }
});

// home.a11y.test.js
const { axe } = require('jest-axe');

describe('Homepage accessibility', () => {
    beforeEach(async () => {
        await page.goto('http://localhost:3000');
        await page.waitForSelector('main');
    });

    it('should have no WCAG A or AA violations', async () => {
        const results = await axe(page, {
            runOnly: {
                type: 'tag',
                values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']
            }
        });

        expect(results.violations).toHaveLength(0);
    });

    it('should have no best-practice violations', async () => {
        const results = await axe(page, {
            runOnly: {
                type: 'tag',
                values: ['best-practice']
            }
        });

        expect(results.violations).toHaveLength(0);
    });

    it('should pass text spacing check', async () => {
        // Test that text remains readable with increased spacing
        await page.addStyleTag({
            content: `
                * { line-height: 1.5 !important; }
                * { letter-spacing: 0.12em !important; }
                * { word-spacing: 0.16em !important; }
            `
        });

        const results = await axe(page, {
            rules: { 'text-spacing': { enabled: true } }
        });

        expect(results.violations).toHaveLength(0);
    });
});

Expected output: Tests run automatically on each test run. Violations cause the test to fail with detailed reports of which elements violate which WCAG criteria and how to fix them.

Code Example: Custom Rule Configuration

// a11y.config.js
const axeConfig = {
    // Include only WCAG 2.2 AA rules
    runOnly: {
        type: 'tag',
        values: [
            'wcag2a',
            'wcag2aa',
            'wcag21a',
            'wcag21aa',
            'wcag22a',
            'wcag22aa'
        ]
    },

    // Exclude specific rules known to have issues
    rules: {
        // Custom component with known contrast issue (tracked in issue #123)
        'color-contrast': { enabled: false },
        // Skip due to third-party widget
        'region': { enabled: false }
    },

    // Custom checks
    checks: [
        {
            id: 'custom-label-check',
            evaluate: (node) => {
                const label = node.querySelector('label');
                const input = node.querySelector('input');
                return label && input && label.getAttribute('for') === input.id;
            }
        }
    ]
};

// Usage in tests
export const createAxeInstance = () => configureAxe(axeConfig);

Expected output: Tests focus on relevant WCAG levels. Known issues are excluded with documentation references. Custom checks validate project-specific patterns.

Code Example: CI/CD Integration with GitHub Actions

name: Accessibility Checks

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  a11y:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        page: [
          '/',
          '/products',
          '/products/123',
          '/checkout',
          '/account'
        ]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build project
        run: npm run build

      - name: Start preview server
        run: |
          npm run preview &
          sleep 5

      - name: Run axe-core on ${{ matrix.page }}
        run: |
          npx @axe-core/cli http://localhost:4173${{ matrix.page }} \
            --exit \
            --save report-${{ matrix.page }}.json

      - name: Upload axe report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: axe-report-${{ matrix.page }}
          path: report-${{ matrix.page }}.json

  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

Expected output: Each Pull Request triggers automated accessibility scans across all key pages. Results are uploaded as artifacts. Pull requests with violations are flagged for review.

Common Mistakes

  1. Enabling too many or too few rules — Focus on WCAG A and AA rules. Too many rules produce noise. Too few miss important issues.
  2. Not handling false positives — Some rules produce false positives for common patterns. Document and exclude them with a tracking issue reference.
  3. Testing only the homepage — Accessibility issues vary by page. Test all unique templates and user flows.
  4. Ignoring the results — Running tests but not reviewing or acting on the results defeats the purpose.
  5. Not testing dynamic content — Single-page apps with JavaScript-rendered content need special handling. Wait for content to render before testing.
  6. Failing to establish a baseline — Without a baseline, teams cannot measure progress or detect regressions meaningfully.
  7. Not training the team — Automated tools are only useful if the team understands how to read and fix violations.

Practice Questions

  1. What is the industry standard library for automated accessibility testing? axe-core (from Deque Systems).
  2. How do you exclude a known accessibility issue from automated tests? Disable the specific rule in the axe configuration with a comment referencing the tracking issue.
  3. Why should automated testing be part of the CI/CD pipeline? To catch accessibility regressions before they reach production, when they are cheapest to fix.
  4. What is the difference between axe-core's "violations" and "incomplete" results? Violations are confirmed accessibility issues. Incomplete means axe could not determine if there is a violation and requires manual review.
  5. Challenge: Set up a complete automated accessibility testing pipeline for a small project. Use axe-core for WCAG testing, Lighthouse CI for performance and accessibility scoring, and configure GitHub Actions to run on pull requests. Include at least 5 page templates. Generate a combined report with violation details and fix guidance.

FAQ

Can automated accessibility testing replace manual testing?

No. Automated tools catch 30-50 percent of accessibility issues. Manual testing with screen readers and keyboard navigation is essential for the remaining issues.

How do I handle third-party widgets that fail accessibility checks?

Exclude the third-party widget from automated tests, document the issue, and work with the vendor to fix it. Use aria-describedby or wrapping elements to improve accessibility where possible.

What is the difference between axe-core and Pa11y?

axe-core focuses on accurate violation detection with low false positives. Pa11y provides a simpler interface and can generate HTML reports. Both use the same underlying accessibility rules.

How do I test dynamically loaded content?

Wait for the content to render before running axe-core. Use waitForSelector or setTimeout to ensure the DOM is stable before testing.

Should I fail the build on every violation?

Start with a warning-only phase, fix all violations, then switch to fail-on-violation. Use a baseline to track known violations and prevent new ones.

Mini Project

Create a complete automated accessibility testing infrastructure for a sample web application. Set up: axe-core tests for 5 key pages (homepage, product listing, product detail, checkout, account), Lighthouse CI configuration, a GitHub Actions workflow that runs tests on every PR, a custom axe configuration that excludes project-specific known issues, a reporting script that generates a violation summary with screenshots, and a dashboard that tracks violation counts over time. Run the pipeline and produce a sample report with at least 3 violations found and fixed.

What's Next

Continue with Lesson 27: Manual Accessibility Testing to learn manual testing techniques that complement automated tools.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro