Konzept: Entwicklertest
Diese Richtlinie enthält Informationen, die helfen, die ersten Hürden zu überwinden, die sich ergeben, wenn man Entwicklertests und eine Testsuite für das gesamte Projekt erstellt. Außerdem gibt diese Richtlinie Ratschläge, wie man Entwicklertests verbessern kann.
Beziehungen
Zugehörige Elemente
Hauptbeschreibung

Einführung

Der Begriff "Entwicklertest" wird verwendet, um die Testaufgaben zu kategorisieren, die am besten von Softwareentwicklern durchgeführt werden können. Er umfasst auch die Arbeitsergebnisse, die von diesen Aufgaben erstellt werden. Entwicklertests umfassen die Arbeiten, die traditionell einer der folgenden Kategorien zugeordnet sind: Einheitentests, ein Großteil der Integrationstests und verschiedene Aspekte dessen, was häufig als Systemtest bezeichnet wird. Während Entwicklertests traditionell Aufgaben der Disziplin Implementierung zugeordnet werden, haben sie auch Beziehungen zu Aufgaben der Disziplin Analyse und Design.

Der ganzheitliche Ansatz des Entwicklertests hilft, die Risiken, die mit dem traditionell verwendeten spezifischen Ansatz verbunden sind, zu mindern. Beim traditionellen Ansatz des Entwicklertests konzentriert man sich zunächst darauf, zu prüfen, ob alle Einheiten unabhängig voneinander funktionieren. In der späten Phase des Entwicklungszyklus, wenn die Entwicklungsarbeit fast abgeschlossen ist, werden die integrierten Einheiten in einem funktionierenden Subsystem oder System assembliert und zum ersten Mal mit diesen Einstellungen getestet.

Dieser Ansatz hat eine Reihe von Schwächen. Erstens, weil er beim Testen der integrierten Einheiten und später beim Testen der Subsysteme abschnittweise vorgeht, werden die Fehler, die bei diesen Tests identifiziert werden, oft zu spät erkannt. Das führt normalerweise dazu, dass keine Korrekturmaßnahmen ergriffen werden oder die Korrektur sehr viel Nacharbeit erfordert. Diese Nacharbeit ist aufwändig und verlangsamt den Fortschritt in anderen Bereichen. Damit erhöht sich das Risiko, dass das Projekt sich in die falsche Richtung entwickelt oder sogar aufgegeben werden muss.

Wenn Sie starre Grenzen zwischen Einheit, Integration und Systemtest schaffen, erhöht das die Wahrscheinlichkeit, dass grenzübergreifende Fehler von niemandem erkannt werden. Das Risiko erhöht sich proportional, wenn die Zuständigkeit für diese Testtypen eigenständigen Teams zugeordnet wird.

Die Darstellung, die von RUP für Entwicklungstests empfohlen wird, hält den Entwickler an, sich auf die Tests zu konzentrieren, die zu einem bestimmten Zeitpunkt am nützlichsten und geeignetsten sind. Selbst wenn man nur den Bereich einer einzelnen Iteration betrachtet, ist es für den Entwickler normalerweise sinnvoller, möglichst viele Mängel in seinem eigenen Code zu finden, ohne mit dem zusätzlichen Aufwand, der für die Kommunikation mit einer anderen Testgruppe erforderlich ist, belastet zu sein. Wünschenswert ist, die wichtigsten Softwarefehler früh zu ermitteln, unabhängig davon, ob diese Fehler bei der unabhängigen Einheit, der Integration der Einheiten oder der Ausführung der integrierten Einheiten innerhalb eines sinnvollen Endbenutzerszenarios auftreten.

Risiken beim Beginn des Entwicklertests

Viele Entwickler, die versuchen, sehr gründliche Tests zu erstellen, geben kurz danach auf, da sie den Eindruck gewinnen, dass diese Vorgehensweise keinen Nutzen bringt. Außerdem erkennen einige Entwickler, dass sie ihre anfänglich effektiven Entwicklertests inzwischen zu einer nicht mehr handhabbaren Testsuite weiterentwickelt haben, die irgendwann ohnehin aufgegeben wird.

Auf dieser Seite finden Sie Richtlinien, die Ihnen helfen, über die ersten Hürden zu gelangen, und eine Testsuite zu erstellen, mit der Sie diese Falle umgehen können. Weitere Informationen finden Sie im Abschnitt Richtlinie: Automatisierte Testsuites verwalten.

Erwartungen definieren

Diejenigen Entwickler, die Entwicklertests lohnend finden, führen sie durch, diejenigen, die sie als lästige Routinearbeit ansehen, vermeiden sie möglichst. So ist schlicht die Situation bei den meisten Entwicklern in fast allen Branchen. Diesen Umstand als beschämenden Mangel an Disziplin zu geißeln, bringt erfahrungsgemäß keinen Erfolg. Daher sollten Sie als Entwickler erwarten, dass Tests nützlich sind, und alles Nötige dazu tun.

Idealerweise folgen Entwicklertests einem sehr kurzen Kreislauf aus Bearbeitung und Test. Sie nehmen eine kleine Änderung am Produkt vor, z. B. das Hinzufügen einer neuen Methode zu einer Klasse, dann führen Sie die Tests sofort erneut aus. Wenn ein Test fehlschlägt, wissen Sie genau, welches Codesegment fehlerhaft ist. Dieser einfache, beständige Entwicklungsrhythmus ist der größte Vorzug der Entwicklertests. Lange Debug-Sitzungen sollten die Ausnahme sein.

Da es nicht ungewöhnlich ist, dass eine Änderung in einer Klasse etwas in einer anderen Klasse beschädigt, sollten Sie erwarten, nicht nur die Tests für die geänderten Klassen, sondern viele Tests erneut durchführen zu müssen. Idealerweise führen Sie die vollständige Testsuite für Ihre Komponente viele Male pro Stunde durch. Jedes Mal, wenn Sie eine wichtige Änderung vornehmen, müssen Sie die Suite ausführen, die Ergebnisse prüfen und mit der nächsten Änderung fortfahren bzw. die letzte Änderung korrigieren. Gehen Sie davon aus, dass Sie etwas Zeit brauchen, um diesen schnellen Feedback zu ermöglichen.

Tests automatisieren

Eine häufige Testausführung ist bei manuellen Tests nicht praktikabel. Bei einigen Komponenten sind automatische Tests einfach durchzuführen. Ein Beispiel dafür ist eine Speicherdatenbank. Sie kommuniziert mit ihren Clients über eine API und hat keine andere Schnittstelle zur Außenwelt. Ein passender Test können wie folgt aussehen:

  /* Prüfen, ob Elemente höchstens einmal hinzugefügt werden können.
*/
 // Konfiguration
 Database db = new Database();
 db.add("key1", "value1");
 // Test
 boolean result = db.add("key1", "another value");
 expect(result == false);

Die Tests unterscheiden sich in einer Hinsicht vom normalen Clientcode: Die Ergebnisse von API-Aufrufen werden nicht einfach übernommen, sondern geprüft. Wenn es einfach ist, über die API Clientcode zu schreiben, ist es auch einfach, Testcode zu schreiben. Wenn der Testcode nicht einfach zu schreiben ist, erhalten Sie eine Frühwarnung, die besagt, dass die API verbessert werden kann. Das Test-First-Design ist daher mit dem Ansatz von Rational Unified Process, wichtige Risiken früh zu behandeln, konsistent.

Je enger die Komponente mit der Außenwelt verbunden ist, desto einfacher ist sie zu testen. Es gibt zwei allgemeine Fälle: grafische Benutzerschnittstellen und Back-End-Komponenten.

Grafische Benutzerschnittstellen

Nehmen Sie an, dass die Datenbank im obigen Beispiel ihre Daten über einen Callback von einem Benutzerschnittstellenobjekt empfängt. Der Callback wird gestartet, wenn der Benutzer einige Textfelder ausfüllt und eine Taste betätigt. Diesen Vorgang zu Testzwecken viele Male pro Stunde auszuführen, ist zu umständlich. Sie müssen einen Weg finden, die Eingabe programmgesteuert vorzunehmen und quasi das Betätigen der Taste durch Code zu realisieren.

Das Betätigen der Taste bewirkt, dass Code in der Komponente ausgeführt wird. Höchstwahrscheinlich ändert dieser Code den Status einiger Benutzerschnittstellenobjekte. Also müssen Sie auch einen Weg finden, diese Objekte programmgesteuert abzufragen.

Back-End-Komponenten

Nehmen Sie an, dass die zu testende Komponente keine Datenbank implementiert, sondern stattdessen ein Wrapper für eine reale Datenbank auf Platte darstellt. Das Testen mit dieser realen Datenbank kann schwierig sein. Die Installation und Konfiguration der Datenbank kann schwierig sein. Lizenzen für die Datenbank können teuer sein. Die Datenbank kann die Tests so sehr verlangsamen, dass Sie geneigt sein könnten, die Tests nicht oft durchzuführen. In diesen Fällen lohnt es sich, die Datenbank durch eine simplere Komponente zu ersetzen, die lediglich die Tests unterstützt.

Stubs sind auch nützlich, wenn eine Komponente, mit der Ihre Komponente kommuniziert, noch nicht fertig ist. Sie möchten sicherlich vermeiden, dass der Test, den Sie durchführen möchten, davon abhängt, dass ein anderer Entwickler seinen Code fertigstellt.

Weitere Informationen finden Sie im Abschnitt Konzept: Stubs.

Keine eigenen Tools schreiben

Entwicklertests scheinen recht klar zu sein. Sie konfigurieren einige Objekte, setzen einen Aufruf über eine API ab und prüfen das Ergebnis. Wenn die Ergebnisse nicht Ihren Erwartungen entsprechen, kündigen Sie einen Testfehler an. Es empfiehlt sich auch, Tests in irgendeiner Weise zusammenzufassen, damit sie einzeln oder als vollständige Testsuites ausgeführt werden können. Tools, die diese Anforderungen unterstützen, werden als Testgerüste bezeichnet.

Entwicklertests sind klar und die Anforderungen für Testgerüste nicht kompliziert. Wenn Sie allerdings Ihr eigenes Testgerüst schreiben, werden Sie viel mehr Zeit damit verbringen, an diesem Gerüst herumzubessern, als Sie vielleicht ahnen. Es sind viele handelsübliche und Open-Source-Testgerüste verfügbar, und es gibt keinen Grund, nicht eines davon zu verwenden.

Unterstützungscode erstellen

Testcode ist häufig eintönig und enthält Codesequenzen wie die folgende:

    // Nullname nicht zulässig
    retval = o.createName(""); 
    expect(retval == null);    
    // führende Leerzeichen nicht zulässig
    retval = o.createName(" l"); 
    expect(retval == null);    
    // nachgestellte Leerzeichen nicht zulässig
    retval = o.createName("name "); 
    expect(retval == null);    
    // erstes Zeichen darf nicht numerisch sein
    retval = o.createName("5allpha"); 
    expect(retval == null);    

Dieser Code wird folgendermaßen erstellt: Man kopiert eine Prüfung, fügt sie ein und bearbeitet sie, um eine neue Prüfung durchzuführen.

Bei dieser Vorgehensweise haben Sie ein doppeltes Risiko. Wenn die Schnittstelle sich ändert, ist viel Bearbeitungsaufwand erforderlich. (In komplizierteren Fällen ist eine einfache Ersetzung nicht ausreichend.) Außerdem kann die Testabsicht bei kompliziertem Code im Text verloren gehen.

Wenn Sie feststellen, dass Sie sich wiederholen, sollten Sie ernsthaft darüber nachdenken, die Wiederholung in Unterstützungscode auszulagern. Obwohl der oben genannte Code ein einfaches Beispiel ist, ist er in folgender Form einfacher zu lesen und zu verwalten:

  void expectNameRejected(MyClass o, String s) { 
    Object retval = o.createName(s);    
    expect(retval == null); }
 ...
    // Nullname nicht zulässig
 expectNameRejected(o, ""); 
 // führende Leerzeichen nicht zulässig
 expectNameRejected(o, " l"); 
 // nachgestellte Leerzeichen nicht zulässig
 expectNameRejected(o, "name "); 
    // erstes Zeichen darf nicht numerisch sein
expectNameRejected(o, "5alpha"); 

Entwickler, die Tests schreiben, machen häufig den Fehler, zu viele Teile zu kopieren und einzufügen. Wenn Sie den Eindruck haben, dass das auch bei Ihnen der Fall sein könnte, tun Sie ganz bewusst das Gegenteil. Beschließen Sie, alle Textduplikate aus dem Code zu entfernen.

Tests zuerst schreiben

Tests nach dem Code zu schreiben, ist eine lästige Routinearbeit. Man verspürt den Wunsch, die Aufgabe schnell abzuschließen und weiterzuarbeiten. Wenn Tests vor dem Code geschrieben werden, werden sie Teil einer positiven Feedback-Schleife. Während Sie weiteren Code implementieren, sehen Sie, dass immer mehr und schließlich alle Tests abgeschlossen werden können. Dann sind Sie mit Ihrer Arbeit fertig. Entwickler, die Tests zuerst schreiben, scheinen mehr Erfolg zu haben, und der Zeitaufwand ist derselbe. Weitere Informationen zur Priorisierung von Tests finden Sie im Abschnitt Konzept: Test-First-Design.

Tests verständlich gestalten

Sie sollten bedenken, dass Sie oder jemand anderes die Tests zu einem späteren Zeitpunkt ändern muss. Eine typische Situation ist, dass sich bei einer späteren Iteration die Notwendigkeit ergibt, das Verhalten der Komponente zu ändern. Nehmen Sie z. B. an, dass die Komponente einmal eine Quadratwurzelmethode wie die folgende deklariert hat:

double sqrt(double x);

In dieser Version führt ein negatives Argument dazu, dass sqrt NaN ("not a number", siehe IEEE-Standard 754-1985, Standard for Binary Floating-Point Arithmetic) zurückgibt. In der neuen Iteration akzeptiert die Quadratwurzelmethode negative Zahlen und gibt ein komplexes Ergebnis zurück:

Complex sqrt(double x);

Alte Tests für sqrt müssen geändert werden. Dazu müssen Sie verstehen, wie sie funktionieren, und Sie müssen sie aktualisieren, damit sie mit der neuen Quadratwurzelmethode sqrt funktionieren. Beim Aktualisieren von Tests müssen Sie darauf achten, ihr Fehlerermittlungspotenzial nicht zu beeinträchtigen. Sie können z. B. wie folgt vorgehen:

  void testSQRT () {    
    //  Tests für komplexe Zahlen aktualisieren
    // Bei Gelegenheit -- bem
    /* double result = sqrt(0.0); ...     */ }

Die folgende Situation erfordert eine komplexere Vorgehensweise: Die Tests wurden geändert und werden ausgeführt, aber sie testen nicht mehr das, was sie ursprünglich testen sollten. Das Endergebnis nach vielen Iterationen kann eine Testsuite sein, die zu schwach ist, um viele Programmfehler ermitteln zu können. Dieser Vorgang wird auch als Verfall einer Testsuite bezeichnet. Eine solche Suite wird letztendlich aufgegeben, da es sich nicht lohnt, sie zu pflegen.

Sie können das Fehlerermittlungspotenzial eines Tests nur aufrechterhalten, wenn klar ist, welche Testideen ein Test implementiert. Im Gegensatz zu Produktcode wird Testcode wird häufig zu wenig kommentiert, obwohl der Grund für sein Vorhandensein oft schwerer zu verstehen ist.

Der Verfall der Testsuite ist in direkten Tests für sqrt weniger wahrscheinlich als in den indirekten Tests. Es gibt Code, der sqrt aufruft. Für diesen Code gibt es Tests. Wenn sqrt geändert wird, werden einige dieser Tests fehlschlagen. Der Entwickler, der sqrt ändert, muss diese Tests wahrscheinlich ebenfalls ändern. Da er diese Tests jedoch weniger kennt und deren Beziehung zur Änderung weniger klar ist, ist es wahrscheinlicher, dass er die Tests, während er mit seiner Arbeit versucht, sie zu einem erfolgreichen Abschluss zu bringen, in ihrer Aussagekraft schwächt.

Wenn Sie Unterstützungscode für Tests erstellen (wie oben erläutert), müssen Sie mit Sorgfalt vorgehen: Der Unterstützungscode muss den Zweck der Tests, die diesen Code verwenden, klarstellen und darf ihn nicht verschleiern. Ein allgemeiner Einwand gegen objektorientierte Programme ist, dass es keinen echten Ausgangspunkt gibt. Wenn Sie irgendeine Methode in Augenschein nehmen, sehen Sie nur, dass der Methodeninhalt an eine andere Stelle weitergeleitet wird. Eine solche Struktur hat Vorteile, erschwert es jedoch neuen Mitarbeitern, den Code zu verstehen. Es kann leicht geschehen, dass sie Änderungen vornehmen, die falsch sind oder den Code komplexer und fragiler machen. Dasselbe trifft auf Testcode zu, und hier ist die Wahrscheinlichkeit, dass neue Mitarbeiter mit dem Code nicht adäquat umgehen, sogar noch höher. Sie können dieses Problem dadurch vermeiden, dass Sie verständliche Tests schreiben.

Teststruktur mit der Produktstruktur abgleichen

Nehmen Sie an, andere Entwickler haben Ihre Komponente übernommen. Sie müssen einen Abschnitt ändern und möchten daher die alten Tests untersuchen, als Hilfestellung für das neue Design. Sie möchten die alten Tests aktualisieren, bevor sie den Code schreiben (Test-First-Design).

Alle diese guten Ansätze sind nutzlos, wenn sie die passenden Tests nicht finden können. Es bleibt ihnen dann nur, die Änderungen vorzunehmen, zu prüfen, welche Tests fehlschlagen und diese Tests dann zu prüfen. Das trägt zum Verfall der Testsuite bei.

Aus diesem Grund ist es wichtig, dass die Testsuite gut strukturiert ist und die Position der Tests sich aus der Produktstruktur ergibt. Normalerweise ordnen Entwickler Tests in einer parallelen Hierarchie an, mit einer Testklasse pro Produktklasse. Wenn also jemand eine Klasse mit dem Namen Log ändert, weiß er, dass der Name der Testklasse TestLog ist und wo die Quellendatei zu finden ist.

Anforderungen für Kapselung bewusst missachten

Sie können Ihre Tests darauf beschränken, dass Sie wie der Clientcode mit Ihrer Komponente interagieren, und zwar über dieselbe Schnittstelle, die auch der Clientcode verwendet. Dieser Ansatz hat jedoch Nachteile. Nehmen Sie an, Sie testen eine einfache Klasse, die eine doppelt verlinkte Liste verwaltet:

Beispiel für doppelt verlinkte Liste

Abbildung 1: Doppelt verlinkte Liste

Sie testen insbesondere die Methode DoublyLinkedList.insertBefore(Object existing, Object newObject). In einem der Tests können Sie ein Element in die Mitte der Liste einfügen und dann prüfen, ob es eingefügt wurde. Der Test verwendet die obige Liste, um diese aktualisierte Liste zu erstellen:

Beispiel für doppelt verlinkte Liste mit eingefügtem Element

Abbildung 2: Doppelt verlinkte Liste mit eingefügtem Element

Die Richtigkeit der Liste wird wie folgt untersucht:

  // Die Liste hat jetzt einen Eintrag mehr.
 expect(list.size()==3);
 // das neue Element ist an der korrekten Position
 expect(list.get(1)==m); 
 // Prüfen, ob alle anderen Elemente noch vorhanden sind
expect(list.get(0)==a); expect(list.get(2)==z);

Das scheint ausreichend zu sein, ist es aber nicht. Nehmen Sie an, dass die Implementierung der Liste falsch ist und Rückwärtszeiger nicht richtig gesetzt sind. Mit anderen Worten, nehmen Sie an, dass die aktualisierte Liste wie folgt aussieht:

Beispiel für doppelt verlinkte Liste mit Implementierungsfehler

Abbildung 3: Doppelt verlinkte Liste - Implementierungsfehler

Wenn DoublyLinkedList.get(int index) die Liste von Anfang bis Ende abarbeitet (was wahrscheinlich ist), würde der Test diesen Fehler übersehen. Wenn die Klasse die Methoden elementBefore und elementAfter bereitstellt, ist die Fehlersuche einfach:

  // Prüfen, ob alle Links aktualisiert wurden
 expect(list.elementAfter(a)==m);
 expect(list.elementAfter(m)==z);
 expect(list.elementBefore(z)==m);
 //Dies scheitert.
 expect(list.elementBefore(m)==a);

Aber was ist, wenn diese Methoden nicht bereitgestellt werden? Sie könnten ausgefeiltere Sequenzen von Methodenaufrufen planen, die fehlschlagen, wenn der vermutete Fehler auftritt. Die folgende Methode würde funktionieren:

  // Prüfen, ob Rück-Link von Z korrekt ist.
 list.insertBefore(z, x);
 // Wurde fälschlicherweise keine Aktualisierung durchgeführt, wird X
 // direkt hinter A eingefügt.
 expect(list.get(1)==m); 

Allerdings ist solch ein Test arbeitsaufwändiger bei der Erstellung und wahrscheinlich wesentlich schwerer zu pflegen. (Wenn Sie keine guten Kommentare schreiben, wird die Funktionsweise des Tests nicht klar sein.) Es gibt zwei Lösungen:

  1. Die Elemente elementBefore und elementAfter zur öffentlichen Schnittstelle hinzufügen. Dadurch wird die Implementierung jedoch für alle zugänglich, künftige Änderungen werden so erschwert.
  2. Die Zeiger mit den Tests direkt prüfen.

Die letztere ist normalerweise die beste Lösung, selbst für eine einfache Klasse wie DoublyLinkedList und insbesondere für die komplexeren Klassen, die in den Produkten auftreten.

Normalerweise werden Tests in dasselbe Paket gestellt wie die Klassen, die sie testen. Sie erhalten ein geschütztes bzw. ein Friend-Zugriffsrecht.

Charakteristische Fehler im Testdesign

Jeder Test führt eine Komponente aus und prüft, ob die Ergebnisse korrekt sind. Das Testdesign, d. h. die Eingaben die der Test verwendet, und die Art und Weise, in der er auf Richtigkeit prüft, kann gut geeignet sein, Mängel aufzudecken oder diese unbeabsichtigt verdecken. Es folgen einige charakteristische Fehler im Testdesign.

Erwartete Fehler vorab nicht angeben

Nehmen Sie an, es wird eine Komponente getestet, die XML in HTML konvertiert. Hier könnte der Tester der Versuchung erliegen, ein XML-Beispielsegment zu nehmen, zu konvertieren und dann die Ergebnisse in einem Browser zu untersuchen. Wenn die Anzeige keine Fehler aufweist, könnte er das erzielte Ergebnis als offizielles erwartetes Ergebnis speichern und HTML "absegnen". Danach würde der Test dann die tatsächliche Ausgabe der Konvertierung mit den erwarteten Ergebnissen vergleichen.

Diese Vorgehensweise ist riskant. Selbst fortgeschrittene Computerbenutzer sind daran gewöhnt, dem Computer zu vertrauen. Es ist wahrscheinlich, dass Sie Fehler in der Bildschirmanzeige übersehen. (Ganz zu schweigen davon, dass die Browser bezüglich schlecht formatierter HTML sehr tolerant sind.) Wenn Sie die falsche HTML zu den offiziellen erwarteten Ergebnissen machen, garantieren Sie, dass der Test das Problem nie ermitteln kann.

Es ist weniger riskant, die HTML-Segmente noch mal zu prüfen, es besteht jedoch immer noch ein Risiko. Da die Ausgabe kompliziert ist, werden Fehler leicht übersehen. Sie können mehr Mängel ausfindig machen, wenn Sie die erwartete Ausgabe zunächst manuell eingeben.

Hintergrund nicht prüfen

Tests prüfen normalerweise, dass die Elemente, die geändert werden sollten, auch tatsächlich geändert wurden. Die Testersteller vergessen jedoch oft, zu prüfen, ob die Elemente, die nicht angerührt werden sollten, auch tatsächlich nicht angerührt wurden. Nehmen Sie beispielsweise an, dass ein Programm die ersten 100 Sätze in einer Datei ändern soll. In diesem Fall macht es Sinn, sicherzustellen, dass der 101. Satz nicht geändert wurde.

In der Theorie prüfen Sie, dass keine im Hintergrund ausgeführten Komponenten - das gesamte Dateisystem, der gesamte Speicher, alle über das Netz erreichbaren Komponenten - verändert wurden. In der Praxis müssen Sie jedoch die Komponenten, die Sie prüfen können, sorgfältig auswählen. Es ist sehr wichtig, diese Auswahl zu treffen.

Persistenz nicht prüfen

Nur weil die Komponente Ihnen mitteilt, dass eine Änderung durchgeführt wurde, heißt das nicht, dass diese Änderung tatsächlich in der Datenbank festgeschrieben wurde. Sie müssen die Datenbank auf anderem Wege prüfen.

Keine Vielfältigkeit bereitstellen

Es kann ein Test entworfen werden, um die Wirkung von drei Feldern in einem Datenbanksatz zu überprüfen, aber es müssen viele andere Felder ausgeführt werden, um den Test auszuführen. Tester neigen dazu, immer wieder dieselben Werte für diese vermeintlich irrelevanten Felder zu verwenden. Beispielsweise verwenden sie den Namen ihrer Freundin oder ihres Freundes in einem Textfeld oder die Zahl 999 in einem numerischen Feld.

Das Problem ist, dass scheinbar unwichtige Dinge manchmal sehr wohl wichtig sein können. Hin und wieder tritt ein Programmfehler auf, der auf einer unbekannten Kombination unwahrscheinlicher Eingaben beruht. Wenn Sie immer dieselben Werte verwenden, haben Sie keine Chance, diese Fehler zu ermitteln. Wenn Sie die Eingaben ständig variieren, sind Ihre Chancen einfach größer. In den meisten Fällen ist der Aufwand, eine andere Zahl als 999 oder einen anderen Namen zu verwenden, nicht erwähnenswert. Wenn das Ändern der Werte in Tests also leicht zu bewerkstelligen ist und einen potenziellen Nutzen hat, tun Sie es.

Es gibt noch einen anderen Vorteil. Es kann z. B. durchaus passieren, dass das Programm das Feld X anstelle des Feldes Y verwendet. Wenn beide Felder den Wert "Dawn" haben, kann der Fehler nicht ermittelt werden.

Unrealistische Daten verwenden

Es ist üblich, in Tests erfundene Daten zu verwenden. Diese Daten sind oft unrealistisch einfach. Beispielsweise können Kundennamen Mickey, Snoopy und Donald sein. Da diese Daten sich von Eingaben realer Benutzer unterscheiden - beispielsweise sind sie typischerweise kürzer - enthalten sie möglicherweise nicht die Mängel, die von realen Kunden gesehen werden können. Beispielsweise kann bei Namen, die aus nur einem Wort bestehen, nicht erkannt werden, dass der Code keine Namen mit Leerzeichen unterstützt.

Daher ist es klug, ein wenig zusätzliche Zeit in die Verwendung realistischer Daten zu investieren.

Übersehen, dass der Code keine Operation ausführt

Nehmen Sie an, Sie initialisieren einen Datenbanksatz mit null, führen eine Berechnung durch, die null ergibt und im Satz gespeichert wird, und prüfen dann, ob der Satz null ist. Was hat dieser Test gezeigt? Möglicherweise wurde die Berechnung überhaupt nicht durchgeführt. Möglicherweise wurde nichts gespeichert, und der Test konnte darüber keine Aussage machen.

Dieses Beispiel klingt unwahrscheinlich. Aber dieser Fehler kann auf subtilere Weise in Erscheinung treten. Nehmen Sie an, Sie schreiben einen Test für ein kompliziertes Installationsprogramm. Der Test soll sicherstellen, dass alle temporären Dateien nach einer erfolgreichen Installation entfernt wurden. Aufgrund all der Optionen des Installationsprogramms in diesem Test wurde jedoch eine bestimmte temporäre Datei nicht erstellt. Mit Sicherheit handelt es sich um die Datei, die das Programm nicht entfernt hat.

Übersehen, dass der Code die falsche Operation ausführt

Manchmal führt ein Programm die richtige Operation aus dem falschen Grund aus. Ein einfaches Beispiel dafür ist der folgende Code:

  if (a < b && c)     
     return 2 * x;
 else    
     return x * x;

Der logische Ausdruck ist falsch, und Sie haben einen Test geschrieben, der bewirkt, dass eine falsche Bewertung vorgenommen und die falsche Abzweigung genommen wird. Leider hat die Variable X rein zufällig den Wert 2 im Test. Also ist das Ergebnis der falschen Abzweigung zufällig richtig und entspricht genau dem Ergebnis, dass die richtige Abzweigung ergeben hätte.

Sie sollten bei jedem erwarteten Ergebnis prüfen, ob es eine Möglichkeit gibt, dass das Ergebnis auf falschem Wege entstanden sein könnte. Es ist zwar oft unmöglich, dies herauszufinden, manchmal aber hat man diese Möglichkeit.