Property-basiertes Testen in TypeScript: Wie Sie Fehler finden, die Sie nicht gesucht haben

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum Unit-Tests nicht vor allem schützen

\n\n

Stellen Sie sich vor, Sie haben eine Funktion zum Sortieren eines Arrays geschrieben. Sie haben sie mit einem Dutzend Unit-Tests abgedeckt: leeres Array, ein Element, bereits sortierte Liste, umgekehrte Reihenfolge. Die Tests sind grün. Sie sind sich Ihres Codes sicher. Aber eine Woche später stürzt die Produktion ab – bei einem Array mit 1000 Elementen, Duplikaten und negativen Zahlen. Kommt Ihnen das bekannt vor?

\n\n

Traditionelles Testen von TypeScript mit Beispielen (example-based testing) prüft nur die Szenarien, die Sie sich ausgedacht haben. Was aber, wenn ein Fehler in einer Kombination lauert, an die Sie nicht gedacht haben? Hier kommt das Property-basierte Testen (eigenschaftsbasiertes Testen) ins Spiel – ein Ansatz, der automatisch Tausende von zufälligen Eingabedaten generiert und die Invarianten Ihres Codes überprüft.

\n\n

In diesem Artikel schauen wir uns an, wie Sie Property-basiertes Testen in TypeScript mit der Bibliothek fast-check anwenden, welche Eigenschaften es zu prüfen gilt und wie dies den Entwicklungsansatz verändert.

\n\n

Was ist Property-basiertes Testen?

\n\n

Anstatt zu schreiben:

\n
it('sortiert [3, 1, 2] → [1, 2, 3]', () => {\n  expect(sort([3, 1, 2])).toEqual([1, 2, 3]);\n});\n
\n\n

Beschreiben Sie eine Eigenschaft (Property) – eine Aussage, die für alle korrekten Eingabedaten wahr sein muss:

\n
it('ein sortiertes Array ändert die Reihenfolge der Elemente nicht', () => {\n  fc.assert(\n    fc.property(fc.array(fc.integer()), (arr) => {\n      const sorted = sort(arr);\n      // Eigenschaft 1: Die Länge bleibt erhalten\n      expect(sorted.length).toBe(arr.length);\n      // Eigenschaft 2: Jedes Element des ursprünglichen Arrays ist vorhanden\n      // Eigenschaft 3: Das Array ist aufsteigend sortiert\n      for (let i = 1; i < sorted.length; i++) {\n        expect(sorted[i - 1]).toBeLessThanOrEqual(sorted[i]);\n      }\n    })\n  );\n});\n
\n\n

Die Bibliothek fast-check generiert selbstständig Hunderte von zufälligen Arrays (einschließlich leere, mit NaN, mit Gleitkommazahlen, mit riesigen Werten) und führt Ihren Test mit jedem aus. Wenn auch nur einer fehlschlägt, erfahren Sie von dem Problem.

\n\n

Warum ist das gerade in TypeScript wichtig?

\n\n

TypeScript bietet statische Typisierung, aber diese garantiert nicht die Korrektheit der Logik. Property-basiertes Testen von TypeScript schließt die Lücke zwischen „die Typen passen“ und „der Code funktioniert richtig“. Besonders nützlich ist es für:

\n\n
    \n
  • Funktionen zur Datentransformation (map, filter, reduce)
  • \n
  • Parser und Validierer
  • \n
  • Algorithmen mit nicht-trivialer Logik
  • \n
  • Funktionen, die mit Grenzwerten arbeiten
  • \n
\n\n

Die Bibliothek fast-check: Schnellstart

\n\n

Installieren Sie fast-check in Ihrem Projekt:

\n
npm install --save-dev fast-check\n
\n\n

Grundlegende Syntax:

\n
import fc from 'fast-check';\n\ndescribe('Eigenschaften der Funktion reverse', () => {\n  it('reverse(reverse(arr)) === arr', () => {\n    fc.assert(\n      fc.property(fc.array(fc.anything()), (arr) => {\n        expect(reverse(reverse(arr))).toEqual(arr);\n      })\n    );\n  });\n});\n
\n\n

Hier ist fc.array(fc.anything()) ein Generator für beliebige Arrays. Sie können Generatoren kombinieren:

\n
    \n
  • fc.integer() – ganze Zahlen
  • \n
  • fc.string() – Zeichenketten
  • \n
  • fc.record({ name: fc.string(), age: fc.integer({ min: 0, max: 120 }) }) – Objekte
  • \n
  • fc.oneof(...) – zufällige Auswahl aus mehreren Generatoren
  • \n
\n\n

Welche Eigenschaften prüfen? 5 praktische Beispiele

\n\n

1. Invarianz (Umkehrbarkeit)

\n

Jede Operation, die eine Umkehrung hat, sollte umkehrbar sein:

\n
it('Serialisierung und Deserialisierung von JSON', () => {\n  fc.assert(\n    fc.property(fc.object(), (obj) => {\n      const json = JSON.stringify(obj);\n      const parsed = JSON.parse(json);\n      expect(parsed).toEqual(obj);\n    })\n  );\n});\n<                

Blogs

Buchempfehlungen