API- und Performance-Testing mit k6 und BDD – 4 Fliegen mit einer Klappe

🕒 Lesedauer: 9 Minuten

"Ich muss API- und Performance-Tests gegen dasselbe Backend machen... Ich hab doch so schon keine Zeit, dann mache ich eben beides gleichzeitig. Die Fachabteilung soll verstehen, was ich da mache? Ja ne ist klar."

Warum auch nicht? API-Tests und Performance-Tests haben oft große Schnittmengen – warum sie also nicht kombinieren? Wenn man sich die Teststrategie clever überlegt, kann man zwei Fliegen mit einer Klappe schlagen. Oder in diesem Fall: Vier.

Denn neben funktionalen API-Tests und nicht-funktionalen Performance-Tests gibt es zwei weitere Aspekte, die oft zu kurz kommen: Testbarkeit und Wartbarkeit. Hier kommt das Service Object Model ins Spiel – eine Architektur, die hilft, Testcode strukturiert, wiederverwendbar und skalierbar zu halten.

Anstatt für API- und Performance-Tests zwei völlig unterschiedliche Ansätze zu verfolgen, kann man eine gemeinsame Basis schaffen, die beides unterstützt. Und mit k6 als Testing-Tool lassen sich Performance-Tests genauso einfach schreiben wie funktionale API-Tests. Und um den Sack schließlich zu zu machen –  in einer BDD-ähnlichen Art und Weise damit man sie auch ohne technisches Detailwissen verstehen kann.

Doch bevor wir ins Detail gehen, schauen wir uns erstmal die einzelnen Komponenten an: API-Testing, Performance-Testing, BDD, k6 und das Service Object Model. Danach setzen wir alles zusammen – inklusive automatisierter Berichte in GitHub Actions.

Image
API und Performance Testing
Quelle: The Valuable Dev

API-Testing – Die Grundlage für zuverlässige Services

APIs sind das Herzstück moderner Anwendungen. Egal ob Web-Apps, Mobile-Apps oder Microservices – sie alle kommunizieren über Schnittstellen. Wenn die API nicht sauber funktioniert, gibt es Probleme, und die werden meistens erst dann bemerkt, wenn Nutzer sich beschweren. Genau das soll API-Testing verhindern.

Dabei geht es nicht nur darum, ob ein Endpunkt grundsätzlich erreichbar ist. Viel wichtiger ist, dass er konsistente und korrekte Antworten liefert, mit verschiedenen Eingaben klarkommt und auch im Fehlerfall sinnvoll reagiert. Ein solider API-Test prüft, ob Response-Codes stimmen, Daten richtig verarbeitet werden und die Authentifizierung sauber funktioniert. Das hilft nicht nur Entwicklern, sondern spart auch im Testprozess eine Menge Zeit, weil viele Fehler frühzeitig entdeckt werden.

Noch mehr Infos zu API-Tests gibt’s hier: API-Testing: Qualität & Sicherheit für Ihre Schnittstellen

Aber so wichtig funktionale API-Tests auch sind – sie sagen nichts darüber aus, wie gut eine API unter Last arbeitet. Und genau hier kommt das Performance-Testing ins Spiel.

 

Performance-Testing – Wenn die API und Entwickler ins Schwitzen kommen

Image
API und Performance Testing
Quelle: The Valuable Dev

Eine API kann funktional einwandfrei laufen – aber was passiert, wenn plötzlich hunderte oder tausende Nutzer gleichzeitig darauf zugreifen? Läuft alles noch genauso reibungslos wie in einer isolierten Testumgebung, oder beginnt das System zu ruckeln? Vielleicht steigen die Antwortzeiten langsam an, oder bestimmte Anfragen blockieren sich gegenseitig. Im schlimmsten Fall fällt der Dienst komplett aus.

Genau hier setzt Performance-Testing an. Es geht nicht nur darum, ob eine API grundsätzlich funktioniert, sondern wie stabil und schnell sie unter realistischen Bedingungen bleibt. Denn egal ob ein Webshop an Black Friday stöhnt, ein Finanzsystem Millionen von Transaktionen verarbeitet oder eine Streaming-Plattform plötzliche Nutzeranstürme erlebt – Performance-Probleme wirken sich direkt auf die User Experience aus.

Es gibt verschiedene Methoden, um die Belastbarkeit einer API zu prüfen. Man kann sie mit konstanter Last testen, sie gezielt überlasten oder prüfen, wie sie auf plötzliche Traffic-Spitzen reagiert. Jedes dieser Verfahren hilft, unterschiedliche Schwachstellen aufzudecken. Doch am Ende läuft alles auf eine Frage hinaus: Hält die API dem erwarteten Traffic stand? Und wenn nicht – warum nicht?

Ein solides Testkonzept hilft, genau diese Fragen frühzeitig zu beantworten. Ohne einen durchdachten Ansatz bleibt Performance-Testing oft ein reines Ratespiel. Wer sich tiefer mit den Methoden und Best Practices beschäftigen möchte, findet hier eine ausführliche Erklärung: Lasttest und Performancetest - Definition und Arten

 

Service Object Model – Struktur für wiederverwendbare Tests

Doch das Problem ist oft nicht nur das Testen selbst, sondern die Struktur der Tests. Wenn API-Tests und Performance-Tests völlig unabhängig voneinander aufgebaut sind, entstehen doppelte Aufwände und unnötige Redundanzen. Was fehlt, ist eine Architektur, die beides integriert – und genau hier kommt das Service Object Model ins Spiel.

Die Grundidee: API-Zugriffe werden in wieder verwendbaren Service-Objekten gekapselt. Diese Service-Objekte enthalten alle relevanten Methoden, um mit einer API zu interagieren – unabhängig davon, ob der Test funktionale oder Performance-Aspekte überprüft.

Ein Beispiel:

Anstatt direkt im Testcode API-Calls zu schreiben, erstellt man eine Service-Klasse, die diese Anfragen kapselt. Eine „UserService“-Klasse könnte zum Beispiel Methoden enthalten wie createUser(), deleteUser() oder getUserById(). Diese Methoden können dann sowohl in API-Tests als auch in Performance-Tests verwendet werden, ohne dass Code dupliziert wird. Das erfordert natürlich programmatische performance Testing Tools wie k6, JMeter ist beispielsweise nicht geeignet.

Das bringt einige Vorteile:

  • Wiederverwendbarkeit: Einmal geschriebene API-Interaktionen können in verschiedenen Testarten genutzt werden.
  • Klarer Testaufbau: Tests konzentrieren sich auf die Logik, während die API-Kommunikation in den Service-Objekten steckt.
  • Leichtere Wartung: Änderungen an API-Endpunkten müssen nur an einer Stelle angepasst werden.
     

Gerade wenn API- und Performance-Tests kombiniert werden sollen, ist das Service Object Model eine extrem hilfreiche Architektur, um den Aufwand gering zu halten und sauberen Code zu gewährleisten.

Mehr zu diesem Konzept gibt es hier: Service Object Model – Warum es hilft (englisch).

Doch eine gute Struktur ist nur die halbe Miete. Damit Tests nicht nur technisch gut geschrieben, sondern auch leicht verständlich sind, kommt BDD – Behavior-Driven Development ins Spiel.

 

BDD – Tests, die jeder versteht

BDD hilft, Tests nicht nur für Entwickler, sondern auch für Tester, Product Owner und andere Stakeholder verständlich zu machen. Es geht darum, Testfälle in einer klaren, natürlichsprachlichen Syntax zu formulieren, die direkt beschreibt, wie sich ein System verhalten soll. Das bekannteste Format dafür ist Gherkin.

Wie sieht ein klassischer BDD-Test aus?

Ein Beispiel für einen BDD-Test in einem Gherkin-Feature-File könnte so aussehen:

Feature: Benutzerregistrierung   
  Als neuer Nutzer   
  möchte ich mich registrieren können   
  um auf meine Inhalte zugreifen zu können   
  Scenario: Erfolgreiche Registrierung   
    Given ein neuer Nutzer mit gültigen Daten   
    When er sich registriert   
    Then erhält er eine Bestätigung 

Diese Art von Testbeschreibung bringt einige klare Vorteile:

  • Jeder kann sie lesen – Entwickler, Tester und sogar Product Owner.
  • Die Struktur ist einheitlich – Testfälle folgen einem klaren Muster.
  • Es schafft eine gemeinsame Sprache zwischen Business und Technik.
     

Für mehr Details, klicke hier: Behavior Driven Testing - Klare Testprozesse und glückliche Endnutzer!

Allerdings gibt es eine Herausforderung: Man braucht ein zusätzliches Test-Framework (z. B. Cucumber), um Gherkin-Tests in ausführbaren Code zu übersetzen.

 

BDD mit k6 – Eine eigene Lösung für API- und Performance-Tests

k6 ist in erster Linie ein Performance Testing Tool und nicht für klassische funktionale API-Tests oder BDD-Teststrukturen ausgelegt. Von Haus aus unterstützt k6 weder Gherkin noch ein strukturiertes Given-When-Then-Schema. Genau deshalb habe ich einen eigenen kleinen Wrapper geschaffen, um k6 in einer BDD-ähnlichen Weise für API- und Performance-Tests nutzbar zu machen.

Das Repository k6 API and Performance Testing zeigt, wie das geht: Mit eigenen BDD-Funktionen wie scenario, given, when und then bleibt die Teststruktur intuitiv und verständlich, ohne dass ein separates Testframework wie Cucumber erforderlich ist.

Wie sieht ein BDD-Test mit k6 aus?

Hier eine direkte Umsetzung eines BDD-Tests in JavaScript mit k6:

import { check } from "k6"; 
import { MainPageService } from "../services/main-page.service.js"; 
import { given, scenario, then, when } from "../setup.js"; 
export const options = { 
    vus: 1, 
    duration: "10m", 
    iterations: 1, 
    thresholds: { 
        checks: [{ threshold: "rate == 1.00", abortOnFail: false }], 
    }, 
}; 
export default function () { 
    scenario( 
        "Die Startseite enthält 'Wir unterstützen Sie dabei, alle Ebenen Ihres Testprozesses nachhaltig zu optimieren'", 
        () => { 
            const mainPage = new MainPageService(); 
            const request = mainPage.getMainPage(); 
            
            given(() => { 
                check(true, { 
                    "ein User surft im Internet": (r) => r, // auto-true 
                }); 
            }); 
            when(() => { 
                check(request, { 
                    "ein User betritt die Startseite von 'qytera.de'": (r) => r.status === 200, 
                }); 
            }); 
            then(() => { 
                check(request, { 
                    "die Startseite enthält den Text 'Wir unterstützen Sie dabei, alle Ebenen Ihres Testprozesses nachhaltig zu optimieren'": 
                        (r) => 
                            r.body.includes( 
                                "Wir unterstützen Sie dabei, alle Ebenen Ihres Testprozesses nachhaltig zu optimieren" 
                            ), 
                }); 
            }); 
        } 
    ); 
} 

Der große Vorteil dieses Ansatzes ist, dass die BDD-Struktur mit given, when, then direkt in JavaScript abgebildet wird, ohne dass eine zusätzliche Testumgebung nötig ist. Gleichzeitig bleibt der Test lesbar, modular und wiederverwendbar – sowohl für funktionale API-Tests als auch für Performance-Tests. Die einzelnen BDD Schritte könnten in Tests wiederum auch zusammengefügt werden, um die Tests auch nicht nicht-Techis leichter schreibbar zu machen. Entwickler und Tester, die bereits mit k6 vertraut sind, sollten hiermit keine Probleme haben.

 

Von API-Tests zu Performance-Tests – Alles integriert in einer GitHub Action

Die Teststrategie, die wir hier im einzelnen zusammengebaut haben, bringt nur dann echten Mehrwert, wenn sie automatisiert ist. Manuelle Testläufe oder separate Testpipelines für API- und Performance-Tests führen oft zu Inkonsistenzen, Mehraufwand und verzögertem Feedback.

Deshalb macht es Sinn, alle Tests in Pipelines auszuführen – direkt integriert in eine GitHub Action - und so abzubilden, dass sie für jeden verständlich sind (hier erfahren Sie mehr über GitHub). Dadurch laufen Tests automatisch bei jedem Commit oder Merge, Fehler werden früh erkannt, und das Test-Reporting bleibt für wirklich jeden Beteiligten transparent durch meinen k6 Wrapper.

Das Repository k6 API and Performance Testing zeigt genau diesen Ansatz.

Die GitHub Actions-Integration ist so aufgebaut, dass sie zwei Arten von Tests automatisiert ausführt:

1. API-Tests mit k6 ausführen

Das folgende Shell-Kommando startet die funktionalen API-Tests:

./tests/run-api-tests.sh

Dieses Skript sorgt dafür, dass alle API-Endpunkte mit den definierten Testfällen überprüft werden – basierend auf der BDD-Logik mit scenario, given, when und then.

Die Ergebnisse werden ebenso wie der Test-Code in einer BDD-ähnlichen Art und Weise dargestellt.

Image
API Test mit k6

 

2. Performance-Tests mit k6 starten

Für Performance-Tests gibt es ein separates Skript:

./tests/run-performance-tests.sh

Hier werden die gleichen Service-Objekte genutzt, allerdings mit eigenständigen Lastszenarien und spezifischen Testfällen für Performance-Tests. So wird sichergestellt, dass die API nicht nur korrekt funktioniert, sondern auch unter realistischen Lastbedingungen stabil bleibt.

3. Durch die Integration GitHub Actions, bieten sich mehrere Vorteile:

  • Automatische Testausführung bei jedem Commit oder Pull Request
  • API- und Performance-Tests in einem einzigen Workflow, wenn gewünscht
  • Visuelles Reporting direkt in GitHub
     

Die CI/CD-Pipeline sorgt dafür, dass Funktionalität und Performance gemeinsam geprüft werden, ohne dass separate Testprozesse erforderlich sind. Die Berichte/Reports ermöglichen es jedem Beteiligten Rückschlüsse auf die aktuelle Funktionalität getesteter Aspekte zu  ziehen, egal ob Entwickler oder Fachbereich. Die BDD-Sprache kann Kommunikations-Missverständnisse überwinden.

 

 

Fazit: API- und Performance-Testing mit k6 und BDD – 4 Fliegen mit einer Klappe

Mit diesem Ansatz lassen sich API- und Performance-Tests in einem gemeinsamen Framework umsetzen – ohne doppelte Arbeit und mit maximaler Verständlichkeit für alle:

Automatisierte API-Tests – Funktionstests mit k6
Performance-Testing parallel zur Entwicklung – Last- und Stresstests ohne separaten Code
Bessere Verständlichkeit durch BDD – Klare Teststruktur mit given, when, then
Integration in DevOps & CI/CD – Automatische Tests in GitHub Actions mit Reporting

Mit diesem Konzept spart man nicht nur Zeit, sondern sorgt auch für eine skalierbare, nachvollziehbare und robuste Teststrategie. Und das Beste: Alles basiert auf Open-Source-Technologien und kann flexibel erweitert werden. Yay 🥳

Veröffentlicht am 03.März 2025

Aktualisiert am 03.März 2025

Matthias Eggert

DevOps Engineer

Matthias Eggert ist ein erfahrener DevOps-Engineer mit starkem Fokus auf Testautomatisierung und Qualitätssicherung. Nach vielen Jahren in der Automobilbranche, wo er sicherheitskritische Systeme wie Bremssysteme und Batteriemanagementlösungen betreute, bringt er sein Wissen nun bei Qytera ein. Sein Schwerpunkt liegt auf modernen Testing-Strategien, CI/CD-Pipelines und Cloud-Technologien. Als Jenkins- und AWS-zertifizierter Experte kombiniert er sein tiefes Verständnis für DevOps mit innovativen Testansätzen, um robuste und effiziente Softwarelösungen zu gewährleisten.

Finden Sie weitere interessante Artikel zum Thema: