Listen-APIs wirken einfach, enthalten aber viele problematische Fehler. Wer nur prüft, ob sich eine Liste öffnet, übersieht möglicherweise doppelte oder fehlende Datensätze, instabile Reihenfolgen, falsche Gesamtzahlen, defekte Filter und Inkonsistenzen zwischen Seiten.
Listen gibt es fast überall: Benutzer, Bestellungen, Zahlungen, Protokolle, Ereignisse, Produkte, Aufgaben und Kommentare. Mangelhafte Paginierung, Filterung oder Sortierung lässt ein Produkt chaotisch wirken: Datensätze verschwinden oder erscheinen doppelt, Filter reagieren unerwartet, die Reihenfolge wechselt und die nächste Seite zeigt widersprüchliche Daten.
Warum APIs diese Funktionen brauchen
Bei wenigen Datensätzen kann ein Server alle zurückgeben. Mit wachsender Datenmenge werden Antworten umfangreich, die Darstellung für den Client aufwendig und der Ressourcenverbrauch des Backends unnötig hoch.
- →Paginierung liefert Daten in Teilmengen
- →Filterung liefert die benötigte Teilmenge
- →Sortierung legt die Reihenfolge der Einträge fest
Diese Mechanismen arbeiten fast immer zusammen. Ein Benutzer öffnet eine Bestellliste, filtert nach Status, sortiert nach Datum und wechselt zwischen Seiten. Instabilität in nur einer Schicht stört den gesamten Ablauf.
Paginierung
Paginierung teilt einen großen Datensatz in Seiten oder Pakete auf.
- →Offset-basierte Paginierung: page=2, limit=20 oder offset=20&limit=20
- →Cursor-basierte Paginierung: cursor=..., starting_after=..., next_page_token=...
QA muss den Unterschied verstehen.
Offset-Paginierung ist leicht lesbar und in Administrationsoberflächen und gewöhnlichen CRUD-APIs verbreitet. Ihre Schwäche: Seiteninhalte können sich verschieben, wenn Daten zwischen Anfragen geändert werden. Ein Benutzer sieht 20 Datensätze auf Seite eins; jemand fügt einen Datensatz hinzu; Seite zwei enthält dann Duplikate oder überspringt Datensätze.
Cursor-Paginierung ist bei veränderlichen Daten im Allgemeinen robuster. Sie eignet sich für große, häufig aktualisierte Listen, Ereignis-Feeds, Transaktionen und stark belastete Systeme. Visuelle Tests sind schwieriger, weil QA einen Cursor und die Reihenfolge statt einer vertrauten Seitennummer verfolgen muss.
Die wichtigste Konsequenz für QA ist, Paginierung sowohl mit veränderlichen Daten als auch mit einem statischen Datensatz zu testen.
Filterung
Filterung schränkt einen Datensatz anhand von Bedingungen ein.
- →status = active
- →created_at >= date
- →Preis zwischen X und Y
- →owner_id = aktueller Benutzer
- →Suche nach Name oder E-Mail-Adresse
- →Mehrere gleichzeitig angewandte Filter
Diese nützliche Funktion ist zugleich eine häufige Quelle logischer Fehler.
- →Ein Filter wird teilweise ignoriert
- →Zwei Filter verhalten sich gemeinsam anders als einzeln
- →Ein leerer und ein nicht angegebener Filter werden unterschiedlich behandelt
- →Die Beachtung von Groß- und Kleinschreibung ist unerwartet
- →Datumsfilter scheitern wegen Zeitzonen
- →Die Backend-Filterung weicht von den Erwartungen der UI ab
- →Berechtigungen verbergen manche Datensätze, sodass der Benutzer den Filter für defekt hält
Filtertests gehen über die Prüfung einer SQL-Bedingung oder eines Abfrageparameters hinaus. Sie klären, ob das System genau die durch seinen Vertrag zugesagte Teilmenge liefert.
Sortierung
Sortierung legt die Reihenfolge der Ergebnisse fest, häufig nach Datum, Name, Status, Priorität, Preis oder einem anderen Feld.
Der häufigste Fehler ist die oberflächliche Prüfung, ob die Reihenfolge ungefähr richtig aussieht. In der Praxis muss die Sortierung Folgendes sein:
- →Korrekt
- →Stabil
- →Vorhersehbar
- →Mit Paginierung vereinbar
Eine stabile Reihenfolge ist besonders wichtig. Wird nur nach einem Feld mit wiederholten Werten sortiert, etwa gleichen created_at-Zeitstempeln oder Statuswerten, kann die Reihenfolge gleichrangiger Datensätze schwanken. Bei Paginierung sehen Benutzer dadurch möglicherweise doppelte oder fehlende Datensätze auf verschiedenen Seiten.
QA sollte Gleichstände ebenso testen wie die gewöhnliche Sortierung nach einem Feld.
Was für QA wichtig ist
Einige Prüfungen sind in fast jedem Fall sinnvoll.
Für Paginierung:
- →Die erste Seite
- →Die letzte Seite
- →Eine leere Seite
- →Seitengröße = 1
- →Seitengröße beim höchsten zulässigen Wert
- →Seitengröße oberhalb des erlaubten Limits
- →Wechsel zwischen benachbarten Seiten
- →Keine Duplikate oder Auslassungen zwischen Seiten
- →Korrekte Werte für has_more, next_cursor und total_count, sofern vorhanden
Für Filterung:
- →Ein einzelner Filter
- →Mehrere Filter gemeinsam
- →Grenzwerte
- →Ein leerer Wert
- →Ein nicht angegebener Filter
- →Ein ungültiger Filterwert
- →Filterung von Feldern mit Zugriffsbeschränkungen
- →Datums- und Zeitfilterung
Für Sortierung:
- →Aufsteigende und absteigende Reihenfolge
- →Sortierung nach verschiedenen Feldern
- →Mehrere Datensätze mit gleichen Werten
- →Sortierung zusammen mit Filtern
- →Sortierung zusammen mit Paginierung
- →Ein nicht unterstütztes Sortierfeld
Häufige Fehlermuster
- →Ein Eintrag erscheint auf der ersten und zweiten Seite
- →Ein Eintrag verschwindet zwischen Seiten
- →total_count weicht von der tatsächlichen Anzahl ab
- →Ein Filter gilt für Datensätze, aber nicht für die Anzahl
- →Die sichtbare Reihenfolge ändert sich zwischen identischen Anfragen
- →Ein neuer Datensatz verschiebt Positionen und stört die Offset-Navigation
- →Das Backend ignoriert eine unbekannte Filter- oder Sortieroption stillschweigend
- →Die UI erwartet eine Reihenfolge, die API liefert eine andere
- →Datumsfilter liefern wegen Zeitzonen unterschiedliche Ergebnisse
- →Filter und Zugriffskontrollen beeinflussen sich unerwartet
Das ist besonders wichtig, wenn Listen die zentrale Arbeitsoberfläche des Produkts sind. Schon geringe Instabilität kann das Vertrauen der Benutzer schnell beeinträchtigen.
Praktisches Beispiel
Betrachten wir eine API für Bestelllisten:
- →GET /orders?status=paid&sort=created_at_desc&limit=20
QA sollte prüfen:
- →Es werden nur bezahlte Bestellungen zurückgegeben
- →Bestellungen sind nach Datum in der angeforderten Richtung sortiert
- →Zwei Bestellungen mit gleichem created_at behalten eine stabile relative Reihenfolge
- →Die nächste Seite enthält keine Duplikate oder Auslassungen
- →total_count entspricht, sofern vorhanden, dem gefilterten Datensatz
- →Die Paginierung verhält sich vorhersehbar, wenn während der Navigation eine neue Bestellung erscheint
- →Ungültige Sortier- oder Statuswerte werden klar behandelt, ohne den Dienst zu beschädigen
Eine Liste kann fast funktionsfähig wirken, während diese Details sie praktisch unzuverlässig machen.
Wichtige Erkenntnisse für QA
- →Paginierung, Filterung und Sortierung sowohl gemeinsam als auch einzeln testen.
- →Listen-APIs versagen oft an Grenzen und Bedingungskombinationen, abseits des Erfolgsfalls.
- →Offset-Paginierung ist einfach, aber empfindlich gegenüber Datenänderungen zwischen Anfragen.
- →Cursor-Paginierung ist bei veränderlichen Daten robuster, verlangt aber einen anderen Testansatz.
- →Stabile Sortierung ist für korrekte Paginierung wesentlich.
- →total_count, has_more, Cursor und Seitengrenzen ebenso sorgfältig prüfen wie die Datensätze selbst.
Weiterführende Informationen
- →In dieser Wissensbasis: Negative API-Tests
- →In dieser Wissensbasis: Methoden, Statuscodes und Header
- →Externe Ressource: Stripe-Paginierung
Ein Datensatz mit 12 Einträgen und bekannten erwarteten Seiten
Die lokale Fixture beginnt mit den IDs 1–12. Der Score ist floor((id−1)/3), sodass drei aufeinanderfolgende Datensätze denselben Score haben. Gerade IDs besitzen den Status open, ungerade closed. Die Sortierung nach Score verwendet die ID als zweiten Schlüssel und macht Gleichstände damit deterministisch. Die folgenden Prüfungen gegen eine frische Fixture ausführen, ohne Schreiboperationen zwischen den Seitenanfragen.
Abfrage Erwartete IDs Gesamtzahl
?sort=score&limit=5&offset=0 1,2,3,4,5 12
?sort=score&limit=5&offset=5 6,7,8,9,10 12
?sort=score&limit=5&offset=10 11,12 12
?sort=score&limit=5&offset=12 (leer) 12
?status=open&sort=score&limit=5&offset=0 2,4,6,8,10 6
?status=open&sort=score&limit=5&offset=5 12 6Die ersten drei Seiten zusammenführen und genau die IDs 1–12 verlangen, ohne Duplikate oder Lücken. Prüfen, dass total den gefilterten Datensatz vor der Paginierung beschreibt, nicht die Länge der aktuellen Seite. In diesem Vertrag muss limit zwischen 1 und 5 liegen, offset ist eine nichtnegative Ganzzahl und nicht unterstützte Sortier-/Filterwerte liefern 400.
Eine stabile Auflösung von Gleichständen erzeugt keinen Snapshot. Ändern sich Daten zwischen Offset-Anfragen, können Datensätze Seitengrenzen überschreiten. Ein separates Experiment sollte den Konsistenzvertrag definieren und zwischen Anfragen Einträge hinzufügen oder löschen; auch Cursor-Paginierung benötigt eine definierte Reihenfolge und Regeln zur Cursor-Gültigkeit. Die bereitgestellte Fixture prüft statisches Offset-Verhalten, nicht diese zusätzlichen Garantien.
Weiterlernen
Lokales Übungslabor herunterladen (ZIP)