Unit Testing - Algorithmus oder Sample basiert?

8

Sagen wir, ich versuche eine einfache Set-Klasse zu testen

%Vor%

Und ich versuche zu testen, dass keine doppelten Werte in der Menge existieren können. Meine erste Option besteht darin, einige Beispieldaten in das Set einzufügen und anhand meiner Kenntnisse der von mir verwendeten Daten auf Duplikate zu prüfen, zum Beispiel:

%Vor%

Meine zweite Möglichkeit besteht darin, generisch auf meinen Zustand zu testen:

%Vor%

Natürlich habe ich in diesem Beispiel eine Set-Implementierung, die überprüft werden kann, und Code zum Vergleichen von Sammlungen (CollectionAssert). Aber was, wenn ich auch nicht hätte? Dieser Code wäre definitiv komplizierter als der der vorherigen Option! Und das ist die Situation, wenn Sie Ihre echte Geschäftslogik für das reale Leben testen.

Zugegeben, das Testen auf erwartete Bedingungen deckt generisch mehr Fälle ab - aber es wird der Implementierung der Logik wieder sehr ähnlich (was sowohl langweilig als auch nutzlos ist - Sie können nicht denselben Code verwenden, um sich selbst zu überprüfen!). Im Grunde frage ich, ob meine Tests so aussehen sollten "1, 2, 3 einfügen, dann etwas über 3 prüfen" oder "1, 2, 3 einfügen und auf etwas im Allgemeinen prüfen"

BEARBEITEN - Um mir das Verständnis zu erleichtern, geben Sie bitte in Ihrer Antwort an, ob Sie OPTION 1 oder OPTION 2 bevorzugen (oder keine, oder dass es vom Fall abhängt, usw.). Zur Klarstellung, es ist ziemlich klar, dass Option 2 in diesem Fall ( IntSet ) in allen Aspekten besser ist. Meine Frage bezieht sich jedoch auf die Fälle, in denen Sie keine alternative Implementierung haben, um zu überprüfen, dass der Code in Option 2 definitiv komplizierter ist als Option 1.

    
Ohad Schneider 17.01.2011, 13:32
quelle

6 Antworten

1
  

Grundsätzlich frage ich ob meine Tests   sollte wie "1, 2, 3 dann einfügen   Überprüfen Sie etwas über 3 "oder" 1 einfügen,   2, 3 und nach etwas suchen   allgemein "

Ich bin kein TDD-Purist, aber es scheint, dass die Leute sagen, dass der Test brechen sollte, wenn die Bedingung, die Sie versuchen zu testen, gebrochen ist. e.i. Wenn Sie einen Test implementieren, der eine allgemeine Bedingung überprüft, wird Ihr Test mehr als ein paar Fälle einbrechen, also ist es nicht optimal.

Wenn ich teste, dass ich keine Duplikate hinzufügen kann, würde ich das nur testen. Also würde ich in diesem Fall sagen, dass ich zuerst wählen würde.

(Aktualisieren)

OK, jetzt haben Sie den Code aktualisiert und ich muss meine Antwort aktualisieren.

Welchen würde ich wählen? Dies hängt von der Implementierung von CollectionAssert.AreEquivalent(new HashSet<int>(values), set); ab. Zum Beispiel behält IEnumerable<T> die Reihenfolge bei, während HashSet<T> dies nicht tut, selbst wenn dies den Test unterbrechen könnte, während dies nicht der Fall sein sollte. Für mich ist first immer noch überlegen.

    
Aliostad 17.01.2011, 13:42
quelle
2

Normalerweise ziehe ich es vor, Anwendungsfälle einzeln zu testen - das funktioniert auf die TDD-Art: "Code ein wenig, teste ein bisschen". Natürlich fangen meine Testfälle nach einiger Zeit an, doppelten Code zu enthalten, also refaktoriere ich. Die tatsächliche Methode, die Ergebnisse zu überprüfen, ist mir egal, solange sie sicher funktioniert und nicht in die Selbstprüfung eingreift. Wenn es also eine "Referenzimplementierung" gibt, gegen die getestet werden muss, ist es umso besser.

Eine wichtige Sache ist jedoch, dass die Tests reproduzierbar sein sollten und sollte klar sein, was jede Testmethode tatsächlich testet . Für mich ist das Einfügen zufälliger Werte in eine Sammlung auch nicht - natürlich, wenn es eine große Menge an Daten / Anwendungsfällen gibt, ist jedes Tool oder jeder Ansatz willkommen, was dazu beiträgt, besser mit der Situation umzugehen, ohne mich zu belästigen in ein falsches Gefühl der Sicherheit.

    
Péter Török 17.01.2011 13:44
quelle
2

Wenn Sie eine alternative Implementierung haben, dann verwenden Sie sie definitiv.

In einigen Situationen können Sie die Neuimplementierung einer alternativen Implementierung vermeiden, aber dennoch die Funktionalität allgemein testen. In Ihrem Beispiel könnten Sie beispielsweise zunächst eine Reihe eindeutiger Werte generieren und anschließend Elemente zufällig duplizieren, bevor Sie sie an Ihre Implementierung übergeben. Sie können testen, dass die Ausgabe Ihrem Ausgangsvektor entspricht, ohne die Sortierung neu implementieren zu müssen.

Ich versuche diesen Ansatz immer dann anzuwenden, wenn es machbar ist.

Update: Im Wesentlichen befürworte ich die Option # 2 des OP. Bei diesem Ansatz gibt es genau einen Ausgangsvektor, der den Test passieren lässt. Mit "Option # 1" gibt es eine unendliche Anzahl von akzeptablen Ausgabevektoren (es wird eine Invariante getestet, aber es wird nicht nach einer Beziehung zu den Eingabedaten gesucht).

    
Oliver Charlesworth 17.01.2011 13:36
quelle
1

Laut xUnit Test Patterns ist es normalerweise günstiger, den Status zu testen des zu testenden Systems. Wenn Sie das Verhalten und die Funktionsweise des Algorithmus testen möchten, können Sie Mock Object Testing verwenden.

Davon abgesehen sind beide Tests als datengesteuerte Tests bekannt. Was normalerweise akzeptabel ist, ist so viel Wissen zu verwenden, wie es die API bietet. Denken Sie daran, diese Tests dienen auch als Dokumentation für Ihre Software. Deshalb ist es wichtig, sie so einfach wie möglich zu halten - was auch immer das für Ihren speziellen Fall bedeutet.

    
Mike 17.01.2011 13:41
quelle
0

Im ersten Schritt sollte die Korrektheit der Add-Methode anhand eines Aktivitätsdiagramms / Ablaufdiagramms demonstriert werden. Der nächste Schritt wäre, die Korrektheit der Add-Methode zu überprüfen, wenn Sie Zeit haben. Dann testet man mit bestimmten Datensätzen, bei denen Duplikate und Nicht-Duplikationen erwartet werden (dh einige Datensätze haben Duplikationen und einige Sets nicht und Du siehst, ob die Datenstruktur korrekt funktioniert.) Es ist wichtig, dass Fälle erfolgreich sind ( keine Duplikate) und um zu überprüfen, ob sie korrekt zum Satz hinzugefügt wurden, anstatt nur nach Fehlerfällen zu suchen (Fälle, in denen Duplikate gefunden werden sollten). Und schließlich generisch überprüfen. Obwohl es jetzt etwas veraltet ist, würde ich vorschlagen, Daten zu konstruieren, um jeden Ausführungspfad in der getesteten Methode vollständig auszuprobieren. An jedem Punkt, an dem Sie eine Codeänderung vorgenommen haben, beginnen Sie den gesamten Regressionstest.

    
Kirt Undercoffer 17.01.2011 14:01
quelle
0

Ich würde mich für den algorithmischen Ansatz entscheiden, aber vorzugsweise ohne eine alternative Implementierung wie HashSet zu verwenden. Sie testen tatsächlich mehr als nur "keine Duplikate" mit der HashSet-Übereinstimmung. Zum Beispiel wird der Test fehlschlagen, wenn irgendwelche Elemente es nicht in die resultierende Menge geschafft haben, und vermutlich haben Sie andere Tests, die dies überprüfen.

Eine sauberere Überprüfung der Erwartung "keine Duplikate" könnte etwa so aussehen:

%Vor%     
Nicole Calinoiu 17.01.2011 14:28
quelle