adesso Blog

Barrierefreiheit ist ein entscheidender Aspekt der modernen Webentwicklung. Sie sorgt dafür, dass digitale Produkte für alle User zugänglich sind, einschließlich Menschen mit Behinderungen. Automatisierte Tests helfen dabei, potenzielle Probleme frühzeitig zu erkennen und sicherzustellen, dass Barrierefreiheit von Anfang an berücksichtigt wird. In diesem Blog-Beitrag zeige ich, wie automatisierte Barrierefreiheitstests mit cypress-axe implementiert werden können. Dabei werfen wir auch einen Blick auf in Kombination relevante Technologien wie das Cypress XRay for Jira Plugin und das Tagging von Tests mit cypress grep. Zusätzlich erfahrt ihr, wie diese Tests in CI/CD-Pipelines wie Jenkins oder GitLab integriert werden können und welche Rolle das Testdatenmanagement dabei spielt. Auch die Bedeutung von manuellen Tests und deren Ergänzung durch automatisierte Verfahren wird thematisiert.

Rechtlicher Hintergrund

Im öffentlichen Bereich gibt es schon länger die Barrierefreie-Informationstechnik-Verordnung (BITV) 2.0. Hier werden die Richtlinien aus der Klassifizierung WCAG 2.1 AA festgesetzt. Nun betrifft es durch das Barrierefreiheitsstärkungsgesetz (BFSG) auch Hersteller, Importeure, Händler sowie Anbieter von Dienstleistungen. Es betrifft vor allem digitale Produkte und Dienstleistungen wie E-Commerce, Bankdienstleistungen, E-Books, audiovisuelle Medien, und elektronische Kommunikation. Auch hier ist die Klassifizierung WCAG 2.1 AA festgesetzt. Hier drohen jedoch ab dem 28. Juni 2025 Strafzahlungen, sofern dies nicht umgesetzt ist.

Voraussetzungen für den leichtgewichtigen Ansatz

Stufe 1: Testautomatisierung mit cypress-axe
  • Lauffähige pipelines in CI/CD-Systemen wie Jenkins/Gitlab/Bitbucket/Github Actions
  • Node installiert
  • Cypress installiert
  • Typescript ausgewählt
Stufe 2: Rückmeldung zu XRay Testmanagement for Jira
  • Jira installiert
  • Technischer Jira-User mit schreibender Berechtigung auf das Projekt
  • XRay Testmanagement for Jira installiert

Einführung in Cypress und cypress-axe: Ein starkes Duo

Cypress ist ein beliebtes End-to-End-Testing-Framework, das durch seine einfache Installation und Bedienbarkeit überzeugt. In Kombination mit der axe-core-Bibliothek lassen sich automatisierte Barrierefreiheitstests nahtlos in bestehende Testabläufe integrieren. axe-core ist eine Open-Source-Bibliothek, die speziell für die Analyse von Webseiten auf Barrierefreiheit entwickelt wurde. Durch die Integration mit Cypress kannst du schnell und effizient Barrierefreiheitstests erstellen, die während deiner normalen Testläufe ausgeführt werden.

Ein Beispiel aus der Praxis: Stellt euch vor, ihr entwickelt eine E-Commerce-Website. Mit Cypress-axe könnt ihr sicherstellen, dass alle wichtigen Elemente wie Navigationsmenüs, Formulare und Buttons zugänglich sind. Ihr schreibt automatisierte Tests, die diese Komponenten auf Barrierefreiheit prüfen. So könnte ein Test beispielsweise überprüfen, ob alle Bilder Alternativtexte haben und ob Formulare korrekt mit Labels versehen sind.

Installation

1. Cypress-axe installieren (für Cypress >= Version 10)

	
	npm install –save-dev cypress-axe
	

2. Kommandos einbauen

	
	import ‘cypress-axe’
		npm install –save-dev cypress-axe
	

in der cypress/support/e2e.js

3. Tsconfig anpassen (bei Nutzung von Typescript in Cypress)

	
	"types": ["cypress", "cypress-axe"]
	

Types um cypress-axe ergänzen

Erste gut wartbare Navigationstests inklusive Barrierefreiheit

Für das Praxisbeispiel habe ich mich für eine fiktive Website entschieden. Die Firma P-A38-42 bietet Smart Home und Smart Gardening Lösungen an. In einem ersten Ansatz soll automatisiert über die Menüpunkte des linken Menüs geklickt werden.

JSON-Datei für URL erstellen

Wir haben für das Beispiel ein verschachteltes Menü, welches wie folgt aufgebaut ist:

  • Produkte
    • Smart Gardening
      • Bewässerungssysteme
    • Smart Home
      • Komplettsysteme
      • Energiemessung
  • Dienstleistungen
    • Schulungen
    • Einbau

Die Datei wird mit dem Namen navigation.json im cpyress Unterordner fixtures abgelegt. Dadurch kann Cypress ohne Datenstrukturbeschreibung direkt darauf zugreifen.

Hinweis: Noch schöner würde es statt als JSON-Datei als YAML-Datei aussehen, hier benötigt man jedoch noch das plugin js-yaml Parser.

Jeder dieser Elemente führt zu einer Unterseite und besitzt ein data-cy HTML-Inline-Element. Dies sorgt dafür, dass die einzelnen Punkte eindeutig bezeichnet sind und unabhängig von den eigentlichen IDs und CSS-Klassen in Cypress angesteuert werden können.

Page Object erstellen

Für eine gute Wartbarkeit nutzen wir von Anfang an das Page Object Model Pattern. Das ist ein Designmuster im Bereich der Testautomatisierung, das dazu dient, die Struktur und Wartbarkeit von Testskripten zu verbessern.

In diesem Muster wird jede Webseite oder Seite einer Anwendung als eine Klasse modelliert, die die Elemente der Seite als Eigenschaften (Buttons oder Textfelder) und die darauf ausführbaren Aktionen als Methoden (Login oder Suche) definiert.

Vorteile
  • Wiederverwendbarkeit: Einmal definierte Seitenobjekte können in mehreren Tests verwendet werden.
  • Wartbarkeit: Änderungen an der Benutzeroberfläche müssen nur in der entsprechenden Page-Objekt-Klasse vorgenommen werden, statt in allen Tests.
  • Klarheit: Testfälle sind einfacher zu lesen und besser strukturiert, da die Logik der Seiteninteraktion von der Testlogik getrennt ist.

Dafür legen wir einen Ordner pageobjects direkt im cypress Verzeichnis an. Hier legen wir nun eine Datei namens topmenue.ts an und füllen Sie mit folgenden Inhalt.

Im Ordner fixtures legen wir nun eine JSON-Datei namens page-menue.json mit folgendem Inhalt an:

	
	{
		  "produkte": {
		 "selector": "[data-cy=produkte]",
		    "text": "Produkte",
		    "url": "/produkte",
		    "children": {
		      "smartGardening": {
		        "selector": "[data-cy=smart-gardening]",
		        "text": "Smart Gardening",
		        "url": "/produkte/smart-gardening",
		        "children": {
		          "bewaesserungssysteme": {
		            "selector": "[data-cy=bewaesserungssysteme]",
		            "text": "Bewässerungssysteme",
		            "url": "/produkte/smart-gardening/bewaesserungssysteme"
		          }
		        }
		      },
		      "smartHome": {
		        "selector": "[data-cy=smart-home]",
		        "text": "Smart Home",
		        "url": "/produkte/smart-home",
		        "children": {
		          "komplettsysteme": {
		            "selector": "[data-cy=komplettsysteme]",
		            "text": "Komplettsysteme",
		            "url": "/produkte/smart-home/komplettsysteme"
		          },
		          "energiemessung": {
		            "selector": "[data-cy=energiemessung]",
		            "text": "Energiemessung",
		            "url": "/produkte/smart-home/energiemessung"
		          }        
		 }
		      }
		    }
		  },
		  "dienstleistungen": {
		    "selector": "[data-cy=dienstleistungen]",
		    "text": "Dienstleistungen",
		    "url": "/dienstleistungen",
		    "children": {
		      "schulungen": {
		        "selector": "[data-cy=schulungen]",
		        "text": "Schulungen",
		        "url": "/dienstleistungen/schulungen"
		      },
		      "einbau": {
		        "selector": "[data-cy=einbau]",
		        "text": "Einbau",
		        "url": "/dienstleistungen/einbau"
		      }
		    }
		  }
		 }
	
Cypress Test erstellen
	
	import Page from '../support/pageObjects/topmenue'
		// Structure for menue items including submenues for recursive using
		interface PageData {
		  selector: string
		  text: string
		  url: string
		  children?: Record<string, PageData>
		}
		describe('Accessibility Tests', () => {
		  const page = new Page()
		  // Load pages from the fixture before each test
		  beforeEach(function () {
		    // Load fixture data
		    cy.fixture('menue-pages').then((pages) => {
		      this.pagesData = pages
		    })
		    // Inject axe for accessibility testing before each test
		    cy.injectAxe()
		  })
		  // Recursive function to test accessibility for a page and its children
		  function testPageAccessibility(pageData: PageData) {
		    it(`should check accessibility for `, function () {
		      // Visit the page
		      page.visit(pageData.url)
		      // Check accessibility with a specific scope or ignoring specific rules
		      cy.checkA11()
		    // Recursively test children if they exist
		    if (pageData.children) {
		      Object.values(pageData.children).forEach(child => {
		        testPageAccessibility(child)
		      })
		    }
		  }
		  // Iterate over each page in the fixture and run the accessibility test
		  it('should run accessibility tests on all pages', function () {
		    const pages: Record<string, PageData> = this.pagesData
		    Object.values(pages).forEach(pageData => {
		      testPageAccessibility(pageData)
		    })
		  })
		  // Optional: Add mobile tests
		  ['iphone-6', 'iphone-12'].forEach(viewport => {
		    it(`should check accessibility for all pages on  viewport`, function () {
		      cy.viewport(viewport)
		      const pages: Record<string, PageData> = this.pagesData
		      Object.values(pages).forEach(pageData => {
		        testPageAccessibility(pageData)
		      })
		    })
		  })
		})
	
Erklärung des Codes

Dieser Code führt Accessibility-Tests für mehrere Seiten einer Anwendung durch, die in einer Fixture-Datei definiert sind. Dabei wird die Accessibility der Seiten sowie ihrer Unterseiten geprüft, und zusätzlich werden die Tests auf verschiedenen Viewports (wie iPhone-6 und iPhone-12) durchgeführt.

Import der Page Object Model (POM)-Klasse
	
	import Page from '../support/pageObjects/page'
	

Dieser Import zieht die Page Object Model-Klasse (POM) aus einer anderen Datei herein.

Definition des PageData-Interfaces
	
	interface PageData {
		  selector: string
		  text: string
		  url: string
		  children?: Record<string, PageData>
		}
	

Hier wird eine Typendefinition für die Struktur der Menü-Elemente erstellt. Das Interface PageData beschreibt die Elemente der Seiten, die getestet werden, inklusive ihres Selectors, des anzuzeigenden Textes, der URL und optional der children. Diese Struktur erlaubt die Rekursion, um hierarchische Menüstrukturen mit Untermenüs (children) abzubilden.

Initialisierung der Testsuite
	
	describe('Accessibility Tests', () => {
		  const page = new Page()
	

describe ist der Block, der die Testsuite definiert. Innerhalb dieses Blocks werden alle Tests zur Barrierefreiheit zusammengefasst. Die Konstante page ist eine Instanz der Page-Klasse, mit der die Seitenaufrufe und möglicherweise andere Interaktionen erfolgen.

beforeEach-Hook zur Vorbereitung der Tests
	
	beforeEach(function () {
		  cy.fixture('menue-pages').then((pages) => {
		    this.pagesData = pages
		  })
		  cy.injectAxe()
		})
	

Der beforeEach-Hook wird vor jedem einzelnen Test ausgeführt. Hier werden zwei Dinge vorbereitet:

  • Daten laden: Mit cy.fixture() werden die Menü-Seiten aus einer JSON-Datei geladen und in this.pagesData gespeichert. Diese Datenstruktur enthält die zu testenden Seiten.
  • Barrierefreiheits-Test-Tool einfügen: Mit cy.injectAxe() wird das Tool axe-core in die Seite geladen, welches zur Überprüfung der Barrierefreiheit verwendet wird.
Rekursive Funktion für Accessibility-Tests
	
	function testPageAccessibility(pageData: PageData) {
		  it(`should check accessibility for `, function () {
		    page.visit(pageData.url)
		    cy.checkA11()
	

Die Funktion testPageAccessibility führt für jede Seite und deren Unterseiten Accessibility-Tests durch.

  • Der it-Block beschreibt den einzelnen Test für die aktuelle Seite.
  • Seite aufrufen: Mit page.visit(pageData.url) wird die zu testende Seite besucht.
  • Barrierefreiheitsprüfung: Mit cy.checkA11() wird die Accessibility der Seite überprüft.
Rekursive Prüfung der Unterseiten
	
	if (pageData.children) {
		  Object.values(pageData.children).forEach(child => {
		    testPageAccessibility(child)
		  })
		}
	

Falls die Seite Unterseiten (children) hat, ruft diese Funktion die testPageAccessibility-Funktion rekursiv für jede Unterseite auf. Dadurch wird sichergestellt, dass auch verschachtelte Seiten auf Barrierefreiheit getestet werden.

Durchführung der Accessibility-Tests für alle Seiten
	
	it('should run accessibility tests on all pages', function () {
		  const pages: Record<string, PageData> = this.pagesData
		  Object.values(pages).forEach(pageData => {
		    testPageAccessibility(pageData)
		  })
		})
	

Hier wird ein Test erstellt, der über alle Seiten iteriert, die in der Fixture-Datei definiert sind. Für jede Seite wird die Funktion testPageAccessibility aufgerufen, die den Test der Barrierefreiheit durchführt. Auf diese Weise wird sichergestellt, dass alle Seiten getestet werden.

Viewport-Tests für mobile Geräte
	
	['iphone-8', 'iphone-12'].forEach(viewport => {
		  it(`should check accessibility for all pages on  viewport`, function () {
		    cy.viewport(viewport)
		    const pages: Record<string, PageData> = this.pagesData    Object.values(pages).forEach(pageData => {
		      testPageAccessibility(pageData)
		    })
		  })
		})
	

Dieser Block führt zusätzliche Tests auf verschiedenen Viewports durch (iPhone-6 und iPhone-12). Mit cy.viewport() wird die Bildschirmgröße simuliert, sodass sichergestellt wird, dass die Seiten auf unterschiedlichen Gerätegrößen korrekt und barrierefrei dargestellt werden.

Für jeden Viewport wird dieselbe rekursive Funktion testPageAccessibility aufgerufen, um sicherzustellen, dass die Barrierefreiheitsregeln sowohl für mobile als auch für Desktop-Ansichten eingehalten werden.

Optimierung mit Cypress XRay for Jira Plugin

Das Cypress XRay for Jira Plugin erweitert die Funktionalität von Cypress, indem es eine direkte Integration mit Jira ermöglicht. XRay ist ein Testmanagement-Tool, das in Jira integriert ist und eine zentrale Verwaltung von Testfällen und -spezifikationen erlaubt. Mit dem Plugin kannst du Barrierefreiheitstests direkt mit User Stories in Jira verknüpfen, was die Rückverfolgbarkeit und Verwaltung der Tests erheblich verbessert.

Ein Beispiel: Eine User Story in Jira beschreibt die Implementierung eines neuen Features auf deiner Website. Mit dem XRay Plugin könnt ihr spezifische Barrierefreiheitstests, die mit diesem Feature verknüpft sind, direkt in Jira dokumentieren und verwalten. So stellt ihr sicher, dass jedes Feature von Anfang an auf Barrierefreiheit getestet wird und die Ergebnisse der Tests direkt in den Entwicklungsprozess einfließen.

Test-Tagging mit cypress grep und parallele Ausführung

Cypress grep ist ein nützliches Tool, um bestimmte Tests basierend auf Tags zu filtern und auszuführen. Das ist besonders hilfreich, wenn ihr Barrierefreiheitstests isolieren und gezielt ausführen möchtet. Du kannst Tags wie @a11y (kurz für Accessibility) verwenden, um Barrierefreiheitstests zu markieren und so sicherzustellen, dass sie unabhängig von anderen Testkategorien ausgeführt werden.

Ein typischer Anwendungsfall wäre die Ausführung von Barrierefreiheitstests als Teil einer CI/CD-Pipeline. In Jenkins oder GitLab kannst du spezifische Testsuiten erstellen, die nur die mit @a11y getaggten Tests ausführen. Dies reduziert durch die Selektion die Laufzeit der Tests und stellt sicher, dass Barrierefreiheitstests nicht übersehen werden.

Ein wichtiger Punkt bei der parallelen Ausführung solcher Tests in CI/CD-Pipelines ist das Testdatenmanagement. Stellt sicher, dass die Tests in einer kontrollierten Umgebung mit konsistenten und nicht konkurrierenden Testdaten ausgeführt werden, um zuverlässige Ergebnisse zu erzielen. Konkurrierende Tests wären beispielsweise, wenn ein Test gerade läuft, der einen expliziten Datensatz sucht, während ein anderer Test genau diesen gerade löscht.

Installation von cypress-grep

Führe folgenden Befehl aus, um das Plugin zu installieren:

	
	npm install --save-dev cypress-grep
	
Konfiguration

Nach der Installation musst du cypress-grep in deiner Cypress-Konfiguration aktivieren.

Öffne oder erstelle die Datei cypress/support/e2e.js und füge folgende Zeile hinzu:

	
	import 'cypress-grep'  // cypress-grep initialisieren
	

Um sicherzustellen, dass die Tests nach Tags gefiltert werden können, kannst du in deiner cypress.config.js (oder .ts bei TypeScript) eine Umgebungsvariable hinzufügen:

	
	module.exports = {
		  env: {
		    grepFilterSpecs: true,  // Filtert nur Spezifikationen, die das Keyword enthalten
		  },
		}
	
Markierung von Tests mit Tags

In deinem Testcode kannst du Tests mit Tags versehen, um sie leichter zu kategorisieren. Zum Beispiel kannst du deine Barrierefreiheitstests mit einem speziellen Tag versehen:

	
	describe('Accessibility Tests', { tags: '@a11y' }, () => {
		  it('should run accessibility tests on all pages', function () {
		    // Dein Testcode hier
		  })
		  it('should check accessibility for mobile viewports', { tags: '@a11y' }, function () {
		    // Test für mobile Barrierefreiheit
		  })
		})
		describe('E2E Tests', { tags: '@e2e' }, () => {
		  it('should perform full E2E test', function () {
		    // E2E Testcode
		  })
		})
	

In diesem Beispiel werden Tests mit dem Tag @a11y als Barrierefreiheitstests markiert und Tests mit @e2e als End-to-End-Tests.

Tests mit grep ausführen

Du kannst nun in der Befehlszeile nur Tests mit bestimmten Tags ausführen. Zum Beispiel, um nur die Barrierefreiheitstests laufen zu lassen:

	
	npx cypress run --env grep=@a11y
	

Um E2E-Tests auszuführen, die mit @e2e markiert sind:

	
	npx cypress run --env grep=@e2e
	

Weiterführendes

Die Prüfung der initialen Seiten beim Laden ist nur ein erster Schritt. Im Idealfall prüft man auch jede Interaktion, bei der beispielsweise extra Elemente geladen werden, Rückmeldungen wie beispielsweise „Toasties“ erscheinen, Lokalisierung oder sonstige Anpassungen.

Für eine iterative Integration kann man auch verschiedene Stufen zur Einführung nutzen. Vorerst nur auf critical impacts oder ein niedrigeres WCAG-Level prüfen. Dann pro Iteration das Prüflevel erhöhen.

Beispiele
	
	// just critical impacts
		  cy.checkA11y(null, {
		    includedImpacts: ['critical']
		  })
		// filter on A-Level instead of AA or AAA
		cy.checkA11y(null,
		  { runOnly: { type: 'tag', values: [wcag2a], }
		 })
	

Auch das Blockieren der Pipeline bei einem Fehler könnte man vorerst unterbinden. Wichtig ist jedoch, dass bei diesen Praktiken das Ganze auch in einem Impediment Backlog / Bereich für technische Schulden aufnimmt. In Kombination mit einer guten Planung kann so gewährleistet werden, dass diese Punkte nicht vergessen werden.

Barrierefreiheitstests und ihre Grenzen: Warum manuelle Tests unverzichtbar sind

Automatisierte Barrierefreiheitstests decken viele, aber längst nicht alle Aspekte der Barrierefreiheit ab. Es wird geschätzt, dass solche Tests maximal 25 % der potenziellen Barrierefreiheitsprobleme erkennen können. Beispielsweise kann ein automatisierter Test erkennen, ob ein Bild einen Alternativtext hat, aber nicht, ob dieser Text sinnvoll und beschreibend ist. Hier kommen manuelle Tests ins Spiel, die eine tiefere Analyse ermöglichen.

Ein Praxisbeispiel: Eine Webseite kann alle formalen Anforderungen an Barrierefreiheit erfüllen, wie korrekte ARIA-Labels und ausreichende Kontraste. Dennoch kann die Benutzerfreundlichkeit (UI/UX) für User mit Behinderungen eingeschränkt sein, wenn die Navigation zu komplex oder nicht intuitiv ist. Manuelle Tests durch Fachleute oder User mit Behinderungen sind notwendig, um solche Probleme zu identifizieren und zu beheben.

Fazit

Automatisierte Barrierefreiheitstests mit cypress-axe bieten eine effektive Möglichkeit, frühzeitig Barrierefreiheitsprobleme in digitalen Projekten zu identifizieren und zu beheben. Durch die Integration von Tools wie dem Cypress XRay for Jira Plugin und cypress grep lassen sich diese Tests nahtlos in den Entwicklungsprozess einbinden und gezielt ausführen. Dennoch bleibt die Bedeutung manueller Tests und eine enge Zusammenarbeit mit UI/UX-Fachleuten unverzichtbar, um eine umfassende Barrierefreiheit sicherzustellen. Indem ihr diese Tools und Best Practices kombiniert, könnt ihr die Durchführungszeit für eine erste Prüfung der Barrierefreiheit deiner Projekte nachhaltig verbessern. Zusätzlich baut ihr auf eine gute Wartbarkeit auf.

Ihr möchtet gern mehr über spannende Themen aus der adesso-Welt erfahren? Dann werft auch einen Blick in unsere bisher erschienenen Blog-Beiträge.

Bild Tobias Kirsch

Autor Tobias Kirsch

Tobias Kirsch ist Senior Software Engineer und seit April 2023 bei adesso. Nach langjähriger Tätigkeit in der IT, unter anderem als Entwickler, gilt seine Leidenschaft der Testautomatisierung in all ihren Ausprägungen. Dabei bereiten ihm die Querschnittsaufgaben zu den angrenzenden Disziplinen wie Testmanagement und Entwicklung große Freude.

Kategorie:

Softwareentwicklung

Schlagwörter:

Barrierefreiheit

Diese Seite speichern. Diese Seite entfernen.