typescript / expert
Snippet
Property-basiertes Testen mit typsicheren Generatoren
Beispielbasierte Tests prüfen Einzelfälle; property-basierte Tests behaupten Invarianten über einen generierten Stichprobenraum. Modelliert man Generatoren als `Gen<T> = () => T`, lassen sie sich mit einfachen Funktionen komponieren — `array(int(...))` erzeugt Integer-Arrays — und TypeScript reicht `T` durch die Kombinator-Kette, sodass die Property den korrekten Typ erhält. Der `check`-Driver durchläuft hunderte Zufallsfälle und wirft beim ersten Gegenbeispiel — er deckt Randfälle auf, die handgeschriebene Tests selten erreichen.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Gen<T> = () => T;const int = (min: number, max: number): Gen<number> =>() => Math.floor(Math.random() * (max - min + 1)) + min;const array = <T>(g: Gen<T>, max = 20): Gen<T[]> =>() => Array.from({ length: int(0, max)() }, g);function check<T>(gen: Gen<T>, prop: (x: T) => boolean, runs = 100): void {for (let i = 0; i < runs; i++) {const sample = gen();if (!prop(sample)) throw new Error(`failed for ${JSON.stringify(sample)}`);}}check(array(int(-50, 50)), (xs) => xs.slice().reverse().reverse().length === xs.length);
Erklärung
1
type Gen<T> = () => T;
Generatoren sind nullary Thunks, die T liefern — leicht komponierbar.
2
const array = <T>(g: Gen<T>, max = 20): Gen<T[]> =>
Kombinator: hebt einen Wert-Generator zu einem Array-Generator an.
3
function check<T>(gen: Gen<T>, prop: (x: T) => boolean, runs = 100): void {
Führt die Property `runs`-mal mit frischen Samples aus.
4
check(array(int(-50, 50)), (xs) => xs.slice().reverse().reverse().length === xs.length);
Doppeltes Umkehren erhält die Länge — über viele Zufalls-Arrays verifizierte Invariante.