Kategorie: logicSchwierigkeit: FortgeschrittenVeröffentlicht: 2024-12-18
Boundary-Conditions verstehen und beheben
Boundary-Conditions treten auf, wenn Code Randfälle oder Extremwerte nicht korrekt behandelt. Diese Fehler können besonders tückisch sein, da sie sich oft nur unter bestimmten Bedingungen zeigen, die während des Testens nicht sofort erkennbar sind.
Häufige Boundary-Conditions-Szenarien
1. Array-Grenzen
// Problem: Keine Überprüfung der Array-Grenzen function holeLetztesElement(array) { return array[array.length]; // Gibt undefined zurück, array[length-1] ist das letzte Element } // Lösung: Korrekte Grenzwertprüfung function holeLetztesElementKorrigiert(array) { if (!Array.isArray(array) || array.length === 0) { throw new Error('Ungültige Array-Eingabe'); } return array[array.length - 1]; }
2. Numerische Bereiche
// Problem: Keine Behandlung numerischer Grenzen function berechneProzent(wert, gesamt) { return (wert / gesamt) * 100; // Schlägt fehl bei gesamt = 0 } // Lösung: Bereichsvalidierung function berechneProzenSicher(wert, gesamt) { if (typeof wert !== 'number' || typeof gesamt !== 'number') { throw new TypeError('Eingaben müssen Zahlen sein'); } if (gesamt === 0) { throw new Error('Prozentberechnung mit Null als Gesamtwert nicht möglich'); } if (wert < 0 || gesamt < 0) { throw new Error('Negative Werte nicht erlaubt'); } return (wert / gesamt) * 100; }
3. String-Operationen
// Problem: Keine Behandlung leerer oder langer Strings function kuerzeString(str, maxLaenge) { return str.substring(0, maxLaenge) + '...'; // Fügt ... auch hinzu, wenn keine Kürzung nötig } // Lösung: Korrekte String-Längenbehandlung function kuerzeStringKorrigiert(str, maxLaenge) { if (typeof str !== 'string') { throw new TypeError('Eingabe muss ein String sein'); } if (maxLaenge < 0) { throw new Error('Maximallänge muss nicht-negativ sein'); } if (str.length <= maxLaenge) { return str; } return str.substring(0, maxLaenge) + '...'; }
Präventionsstrategien
1. Eingabevalidierung
class EingabeValidator { static validiereBereich(wert, min, max, name = 'Wert') { if (typeof wert !== 'number' || isNaN(wert)) { throw new TypeError(`${name} muss eine gültige Zahl sein`); } if (wert < min || wert > max) { throw new RangeError( `${name} muss zwischen ${min} und ${max} liegen, ist aber ${wert}` ); } return wert; } static validiereArrayIndex(array, index, name = 'Index') { if (!Array.isArray(array)) { throw new TypeError('Array erwartet'); } if (!Number.isInteger(index)) { throw new TypeError(`${name} muss eine ganze Zahl sein`); } if (index < 0 || index >= array.length) { throw new RangeError( `${name} außerhalb des gültigen Bereichs: ${index} (Array-Länge: ${array.length})` ); } return index; } static validiereString(str, optionen = {}) { const { minLaenge = 0, maxLaenge = Infinity, name = 'String' } = optionen; if (typeof str !== 'string') { throw new TypeError(`${name} muss ein String sein`); } if (str.length < minLaenge) { throw new RangeError( `${name} muss mindestens ${minLaenge} Zeichen lang sein` ); } if (str.length > maxLaenge) { throw new RangeError( `${name} darf nicht länger als ${maxLaenge} Zeichen sein` ); } return str; } } // Verwendung function verarbeiteDaten(array, index) { EingabeValidator.validiereArrayIndex(array, index); return array[index]; }
2. Bereichsprüfer
class Bereichspruefer { static istImBereich(wert, min, max) { return wert >= min && wert <= max; } static istImBereichExklusiv(wert, min, max) { return wert > min && wert < max; } static begrenze(wert, min, max) { return Math.min(Math.max(wert, min), max); } static normalisiereIndex(index, laenge) { if (index < 0) { return Math.max(0, laenge + index); } return Math.min(index, laenge - 1); } } // Verwendung function holeArrayElement(array, index) { const normalerIndex = Bereichspruefer.normalisiereIndex(index, array.length); return array[normalerIndex]; }
3. Grenzwächter
class Grenzwaechter { static schuetzeArray(array, operation) { if (!Array.isArray(array) || array.length === 0) { return null; } return operation(array); } static schuetzeDivision(zaehler, nenner, fallback = 0) { if (nenner === 0) { return fallback; } return zaehler / nenner; } static schuetzeString(str, operation, fallback = '') { if (typeof str !== 'string' || str.length === 0) { return fallback; } return operation(str); } } // Verwendung const ergebnis = Grenzwaechter.schuetzeDivision(10, 0, Infinity); console.log(ergebnis); // Infinity statt NaN
Teststrategien
1. Grenzwerttests
describe('Grenzwerttests', () => { it('sollte Array-Grenzen korrekt behandeln', () => { const array = [1, 2, 3]; // Test leeres Array expect(() => holeLetztesElementKorrigiert([])).toThrow(); // Test gültige Indizes expect(holeLetztesElementKorrigiert(array)).toBe(3); // Test ungültige Eingabe expect(() => holeLetztesElementKorrigiert(null)).toThrow(); }); it('sollte numerische Grenzen behandeln', () => { // Test Division durch Null expect(() => berechneProzenSicher(5, 0)).toThrow(); // Test negative Werte expect(() => berechneProzenSicher(-1, 100)).toThrow(); // Test gültige Werte expect(berechneProzenSicher(50, 100)).toBe(50); }); });
2. Bereichstests
describe('Bereichstests', () => { it('sollte numerische Bereiche validieren', () => { // Test Werte im Bereich expect(Bereichspruefer.istImBereich(5, 0, 10)).toBe(true); // Test Grenzwerte expect(Bereichspruefer.istImBereich(0, 0, 10)).toBe(true); expect(Bereichspruefer.istImBereich(10, 0, 10)).toBe(true); // Test Werte außerhalb des Bereichs expect(Bereichspruefer.istImBereich(-1, 0, 10)).toBe(false); expect(Bereichspruefer.istImBereich(11, 0, 10)).toBe(false); }); it('sollte Werte korrekt begrenzen', () => { expect(Bereichspruefer.begrenze(-1, 0, 10)).toBe(0); expect(Bereichspruefer.begrenze(5, 0, 10)).toBe(5); expect(Bereichspruefer.begrenze(11, 0, 10)).toBe(10); }); });
3. Randfälle-Tests
describe('Randfälle-Tests', () => { it('sollte String-Randfälle behandeln', () => { const optionen = { minLaenge: 2, maxLaenge: 10 }; // Test leerer String expect(() => EingabeValidator.validiereString('', optionen) ).toThrow(); // Test Mindestlänge expect(() => EingabeValidator.validiereString('a', optionen) ).toThrow(); // Test Maximallänge expect(() => EingabeValidator.validiereString('a'.repeat(11), optionen) ).toThrow(); // Test gültige Strings expect( EingabeValidator.validiereString('hallo', optionen) ).toBe('hallo'); }); });
Beste Praktiken
-
Immer Eingaben validieren
// Schlecht function verarbeite(wert) { return wert * 2; } // Gut function verarbeite(wert) { if (typeof wert !== 'number' || isNaN(wert)) { throw new TypeError('Zahl erwartet'); } return wert * 2; }
-
Schutzklauseln verwenden
// Schlecht function dividiere(a, b) { return a / b; } // Gut function dividiere(a, b) { if (b === 0) { throw new Error('Division durch Null'); } return a / b; }
-
Sonderfälle behandeln
// Schlecht function holeErstesZeichen(str) { return str[0]; } // Gut function holeErstesZeichen(str) { if (!str || typeof str !== 'string') { return ''; } return str[0] || ''; }
Häufige Fallstricke
-
Array-Länge vs. Index
// Häufiger Fehler const arr = [1, 2, 3]; for (let i = 0; i <= arr.length; i++) { // <= ist falsch console.log(arr[i]); // Undefined bei letzter Iteration } // Korrekt for (let i = 0; i < arr.length; i++) { // < ist richtig console.log(arr[i]); }
-
Gleitkomma-Präzision
// Unerwartetes Verhalten console.log(0.1 + 0.2 === 0.3); // false // Lösung: Epsilon-Vergleich verwenden function fastGleich(a, b, epsilon = Number.EPSILON) { return Math.abs(a - b) < epsilon; } console.log(fastGleich(0.1 + 0.2, 0.3)); // true
-
String-Länge vs. Index
// Unicode-Probleme const str = '👋'; console.log(str.length); // 2 (Surrogatpaar) // Lösung: Array.from oder Spread verwenden console.log([...str].length); // 1 (korrekte Länge)