Wie sollte ich eine Klasse testen, die auf realistischen Daten beruht?


8

Ich habe eine Klasse, die die Ergebnisse einer wissenschaftlichen Messung zusammenfasst. Ich baue von Anfang an Unit-Tests ein, habe aber nicht viel Erfahrung mit Unit-Tests und bin mir nicht sicher, welche Verhaltensweisen ich wie testen soll.

Meine Klasse macht drei Arten von Dingen:

  1. Liest Messdaten aus einer Datei (oder einer Zeichenfolge) in ihre Instanzvariablen
  2. Schreibt seine Messdaten in eine Datei oder einen String
  3. Führt Berechnungen für seine Daten durch (z. B. Ermitteln des Durchschnitts einer Reihe von Zahlen)

Mein aktueller Ansatz ist es, eine bekanntermaßen gute Beispieldatendatei in mein testVerzeichnis aufzunehmen. Ein Test liest die Daten aus der Datei ein, übergibt sie an meine Klasse und stellt sicher, dass sie einige grundlegende Sicherheitsüberprüfungen erfüllen. Ein weiterer Test übergibt die Dateidateinamen in meine Klasse, lässt die Klasse lesen, und führt die gleichen Tests. Der Rest der Tests liest die Daten aus der Datei ein, übergibt sie an meine Klasse und überprüft, ob die Ergebnisse der Datenverarbeitungsmethoden korrekt sind, sofern ich über diesen Datensatz Bescheid weiß.

Dies scheint jedoch ziemlich verwirrt zu sein. Die Tests, die (3) überprüfen, setzen implizit voraus, dass das Verhalten von (1) korrekt ist, da die Funktionen in (1) verwendet werden, um die Klasse überhaupt erst zu füllen. Und die Tests von (1) könnten von den umfangreichen Überprüfungen profitieren, die durch die Tests für (3) durchgeführt wurden. Strukturiere ich meine Unit-Tests schlecht oder ist dies nur ein natürliches Ergebnis der Tatsache, dass ich für meine Tests einen bestimmten Datensatz verwenden muss?


2
Willkommen beim Integrationstest. Im Ernst, Sie sind auf dem richtigen Weg. Einige Überlegungen: Wenn Sie möchten, dass Ihr Projekt schnell abgeschlossen wird, versuchen Sie es mit einer Big-Bang-Integration. Wenn Sie wollen in der Lage sein, tatsächlich herauszufinden , was mit Ihrem Programm falsch ist, jetzt und später weiter methodisch Testfälle für jedes Teilsystem zu etablieren.
Andyz Smith

Antworten:


14

Was Sie tun, sind Integrationstests, denn wie Sie wahrscheinlich sehen können, hängen Ihre Tests von anderen Teilen Ihres Codes ab. Das ist in Ordnung, aber es ist gut zu wissen, wenn Sie Artikel / Beispiele / etc. Betrachten. online.

Einige Punkte zu beachten und Dinge zu beachten:

  • Vereinbaren Sie , Act , Assert . Alle Tests haben diese 3 Schritte.
    • Benötigen Sie weitere Schritte? Sie testen wahrscheinlich zu viel für einen Test
    • Nein arrangieren? Sie haben externe Abhängigkeiten, was Tests fast immer unzuverlässig und unzuverlässig macht.
    • Viel Arrangiercode bedeutet normalerweise viele Abhängigkeiten und Abhängigkeiten vom Systemstatus. Das ist ein Rezept für fragilen Code.
    • Kein Akt? Testet oft den Compiler. Überlassen Sie das den Compiler-Entwicklern.
    • Viele Handlungen? Sie testen wahrscheinlich wieder zu viel für einen Test.
    • Keine Behauptung? Oft der Fall, wenn Sie testen möchten, dass "keine Fehler aufgetreten sind". Keine Fehler! = Funktioniert richtig.
    • Viele Behauptungen? Der zu testende Code macht möglicherweise zu viel / berührt zu viele Systeme. Versuchen Sie stattdessen, die einzelnen Bits umzugestalten und zu testen.
  • Tests sollten eigenständig existieren. Vermeiden Sie Szenarien, in denen etwas, das 3 testet, vom Code abhängt, der 1 ausführt . Versuchen Sie stattdessen, den Code, der 1 ausführt, durch Testcode zu ersetzen . In Ihrem Szenario: Versuchen Sie , die Werte manuell zu dem, was Sie wollen , dass sie in dem Test des Vereinbaren Schritt, dann führen Sie das Gesetz Ion (die Berechnungen), dann Assert das Ergebnis.
  • Happy-Path-Tests sind oft Zeitverschwendung. Best-Case-Szenarien funktionieren fast immer. Ihre Tests sollten versuchen, Fehler und Randfälle zu erzwingen.
    • Haben Sie einen Test, der fehlerhafte zu verarbeitende Streams besteht?
    • Haben Sie einen Test, der null / nicht vorhandene Dateinamen für die Verarbeitung besteht?
    • Was passiert, wenn die zu berechnenden numerischen Werte nicht numerisch sind oder wenn die Analyse sehr groß ist oder wenn die Berechnung sehr groß ist?
  • Überraschenderweise bestätigt das Schreiben von Tests selten, dass das, was Sie getan haben, richtig ist. Stattdessen geben sie Ihnen einen Einblick, wie benutzerfreundlich, stabil und flexibel Ihr Design ist.

edit> Dies wurde bereits akzeptiert, aber ich wollte etwas hinzufügen, das ich vor langer Zeit über Pragmatic Unit Testing gelernt habe :

Unit Testing mit Ihrem richtigen BICEP

  • Sind die Ergebnisse richtig ?
  • RICHTIG B oundary Bedingungen
    • C onform zu erwarteten Format
    • O rdered richtig
    • R ange ist richtig
    • R eferenzen auf externe Abhängigkeiten sind sicher
    • E Xistence (null etc.)
    • C ardinalität
    • T IMing (Dinge passieren in der richtigen Reihenfolge, mit dem richtigen Timing)
  • Ich kehre Beziehungen um
  • C ross-Check Ergebnisse mit anderen Mitteln
  • E rrors sind gezwungen ,
  • Die Leistungseigenschaften liegen innerhalb akzeptabler Grenzen

5

Ich denke, dass Ihre Klasse schwer zu testen ist, weil sie zu viele Verantwortlichkeiten hat. Sie erwähnen sie selbst

  • Daten aus Datei lesen
  • Daten in Datei schreiben
  • Berechnung durchführen

Idealerweise sollte eine Klasse eine einzige Verantwortung oder einen einzigen Verantwortungsbereich haben. In diesem Fall sollten Sie auf jeden Fall eine Klasse haben, die nur die Logik zum Durchführen der Berechnungen enthält.

Wenn Sie das hätten, könnten Sie vielleicht eine Funktion wie diese haben:

CalculationResult PerformCalculation(Measurement[] measurements)
{ ... }

Dies ist relativ einfach zu testen, da Sie in einem Test problemlos eine Reihe genau definierter Messungen liefern können.

Ich gehe hier davon aus, dass Sie einmal aus der Datei lesen, um alle Messungen auf einmal zu erhalten. Wenn Sie im Laufe der Zeit neue Messungen sammeln, die in die Datei geschrieben wurden, könnte Ihre Klasse folgendermaßen aussehen:

class Calculator {
    AddMeasurement(Measurement measurement) { ... }

    CalculationResult PerformCalculation() { ... }
}

Immer noch einfach zu testen, da Sie die Klasse während des Tests mit einer Reihe genau definierter Messungen versorgen und das Ergebnis lesen können, ohne vom Dateisystem abhängig zu sein.

Eine andere Klasse könnte die Verantwortung haben, Messdaten aus der Datei zu lesen. Dies könnte wiederum in das Lesen von Daten aus einer Datei und das Parsen der Daten als Messobjekte aufgeteilt werden, um das separate Testen der Parsing-Logik ohne Abhängigkeit vom Dateisystem usw. zu ermöglichen.

Wenn es schwierig ist, einen Komponententest zu schreiben, ist dies oft ein Zeichen dafür, dass Ihre "Einheit" mehrere Verantwortlichkeiten hat, zu viele Abhängigkeiten aufweist oder auf andere Weise nicht FEST ist .


3

Unit-Tests sollten zwei Sätze von Fällen testen:

Die sehr grundlegenden Fälle: Ist die Berechnung korrekt, addieren sich die Summen usw. Verwenden Sie einfache, leicht zu überprüfende Daten für diese Fälle.

Die Randfälle: Liefertermin 29. Februar 2016, Bestellmenge von 999.999, Artikel mit einem Preis von 0,00 USD, GPS für den Nordpol (versuchen Sie, nach Westen zu ziehen!) Usw. usw.


0

Ich denke, viele der Antworten sind zutreffend, in dem Sinne, dass zu viele der wesentlichen Tests so aussehen, als würden sie zu einer Einheitstestinstanz kombiniert. ABER.

Viele Funktionen erfordern komplizierte Daten und führen zu komplizierten Ergebnissen. Die Bildverarbeitung kann beispielsweise eine kompakte Funktion haben, die aus einem Bild eine Maske erzeugt. Ein (ich würde argumentieren, angemessener) Testtreiber würde ein Bild lesen, verarbeiten, eine Bilddatei schreiben und die resultierende Datei mit einer Referenzbilddatei vergleichen.

Das Testen der Integration all dieser Funktionen in das Ziel wäre ein Integrationstest. Das Testen einer einzelnen Funktion Ihres Ziels, einer komplizierten Eingabe zu einer komplizierten Ausgabe, ist ein geeigneter Komponententest.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.