Sollte man die Werte einer Aufzählung mit Unit-Tests testen?

15

Wenn Sie eine Aufzählung nur mit Werten haben (keine Methoden wie in Java) und diese Aufzählung Teil der Geschäftsdefinition des Systems ist, sollte man Unit-Tests dafür schreiben?

Ich dachte, dass sie geschrieben werden sollten, auch wenn sie einfach und überflüssig erscheinen könnten. Ich denke, dass was die Geschäftsspezifikation betrifft, explizit in einem Test geschrieben werden sollte, sei es mit unit / integration / ui / etc. Tests oder unter Verwendung des Typsystems der Sprache als Testmethode. Da die Werte, die eine Aufzählung (z. B. in Java) aus Sicht des Geschäfts haben muss, nicht mit dem Typsystem getestet werden können, sollte es meiner Meinung nach einen Komponententest dafür geben.

Diese Frage ist nicht ähnlich diesen , da es nicht das gleiche Problem wie ich nicht ansprechen. In dieser Frage gibt es eine Business Function (savePeople) und die Person fragt nach der internen Implementierung (forEach). Darin befindet sich eine mittlere Business-Schicht (die Funktion save people), die das Sprachkonstrukt (forEach) kapselt. Hier ist das Sprachkonstrukt (enum) dasjenige, das verwendet wird, um das Verhalten von einem geschäftlichen Standpunkt aus zu spezifizieren.

In diesem Fall stimmt das Implementierungsdetail mit der "wahren Natur" der Daten überein, d. H. Einer Menge (im mathematischen Sinne) von Werten. Sie könnten wohl eine unveränderliche Menge verwenden, aber die gleichen Werte sollten dort immer noch vorhanden sein. Wenn Sie ein Array verwenden, müssen Sie das Gleiche tun, um die Geschäftslogik zu testen. Ich denke, das Rätsel hier ist die Tatsache, dass das Sprachkonstrukt sehr gut mit der Art der Daten übereinstimmt. Ich bin mir nicht sicher, ob ich mich richtig erklärt habe

IS1_SO
quelle
19
Wie genau würde ein Komponententest einer Aufzählung aussehen?
Jonrsharpe
@jonrsharpe Es wird davon ausgegangen, dass die Werte, die sich innerhalb der Aufzählung befinden, die sind, die Sie erwarten. Ich würde es tun, indem ich die Werte der Aufzählung durchlaufe und sie einer Menge hinzufüge, zum Beispiel als Zeichenfolgen. Bestellen Sie dieses Set. Der Vergleich wird mit einer geordneten Liste von Werten durchgeführt, die im Test von Hand geschrieben wurden. Sie sollten zusammenpassen.
IS1_SO
1
@jonrsharpe, ich stelle mir Unit-Tests auch gerne als "Definitionen" oder "Anforderungen" vor, die im Code geschrieben sind. Ein Komponententest einer Aufzählung wäre so einfach wie das Überprüfen der Anzahl der Elemente in der Aufzählung und ihrer Werte. Insbesondere in C #, wo die Aufzählungen keine Klassen sind, sondern direkt Ganzzahlen zugeordnet werden können, kann es sich für Serialisierungszwecke als nützlich erweisen , ihre Werte zu garantieren und nicht zufällig zu programmieren .
Machado
2
IMHO ist es nicht viel nützlicher als zu testen, ob 2 + 2 = 4, um zu überprüfen, ob das Universum richtig ist. Sie testen den Code mit dieser Aufzählung, nicht mit der Aufzählung selbst.
Agent_L

Antworten:

39

Wenn Sie eine Aufzählung nur mit Werten haben (keine Methoden wie in Java) und diese Aufzählung Teil der Geschäftsdefinition des Systems ist, sollte man Unit-Tests dafür schreiben?

Nein, sie sind nur staatlich.

Grundsätzlich ist die Tatsache, dass Sie eine Aufzählung verwenden, ein Implementierungsdetail . Das ist die Art von Dingen, die Sie in der Lage sein möchten, in ein anderes Design umzugestalten.

Das Testen von Aufzählungen auf Vollständigkeit ist analog zum Testen, ob alle darstellbaren ganzen Zahlen vorhanden sind.

Das Testen des von den Enumerationen unterstützten Verhaltens ist jedoch eine gute Idee. Mit anderen Worten, wenn Sie von einer bestandenen Testsuite ausgehen und einen einzelnen Enum-Wert auskommentieren, sollte mindestens ein Test fehlschlagen (Kompilierungsfehler gelten als Fehler).

VoiceOfUnreason
quelle
5
In diesem Fall stimmt das Implementierungsdetail jedoch mit der "wahren Natur" der Daten überein, d. H. Einer Menge (im mathematischen Sinne) von Werten. Sie könnten wohl eine unveränderliche Menge verwenden, aber die gleichen Werte sollten dort immer noch vorhanden sein. Wenn Sie ein Array verwenden, müssen Sie das Gleiche tun, um die Geschäftslogik zu testen. Ich denke, das Rätsel hier ist die Tatsache, dass das Sprachkonstrukt sehr gut mit der Art der Daten übereinstimmt. Ich bin mir nicht sicher, ob ich mich richtig erklärt habe.
IS1_SO
4
@ IS1_SO - nach Ansicht von VOU sollte ein Test jedoch fehlschlagen: Hat es funktioniert? In diesem Fall mussten Sie das Enum nicht speziell testen. Nicht wahr Vielleicht ist das ein Zeichen dafür , dass Sie Ihren Code einfacher modellieren könnten und eine Abstraktion über die ‚wahre Natur‘ der Daten erstellen - zB unabhängig von den Karten in einem Deck, brauchen Sie wirklich eine Darstellung haben [ Hearts, Spades, Diamonds, Clubs] wenn du nur jemals eine karte wenn eine karte rot / schwarz ist?
Anotherdave
1
@ IS1_SO Angenommen, Sie haben eine Enumeration von Fehlercodes, und Sie möchten einen null_ptrFehler auslösen . Das hat jetzt einen Fehlercode über die Enumeration. Die Code-Überprüfung auf einen null_ptrFehler schlägt den Code auch über die Aufzählung nach. So kann es einen Wert von 5(zum Beispiel) haben. Jetzt müssen Sie einen weiteren Fehlercode hinzufügen. Die Aufzählung wurde geändert (sagen wir, wir fügen oben in der Aufzählung eine neue hinzu). Der Wert von null_ptrist jetzt 6. Ist das ein Problem? Sie geben jetzt einen Fehlercode von zurück 6und testen auf 6. Solange alles logisch konsistent ist, geht es Ihnen gut, obwohl diese Änderung Ihren theoretischen Test durchbrochen hat.
Baldrickk
17

Sie testen keine Enum- Deklaration . Sie können testen, ob der Funktionseingang / -ausgang die erwarteten Aufzählungswerte aufweist. Beispiel:

enum Parity {
    Even,
    Odd
}

Parity GetParity(int x) { ... }

Sie schreiben keine Tests, in denen überprüft wird, ob enum Paritydie Namen Evenund definiert Odd. Ein solcher Test wäre sinnlos, da Sie nur wiederholen würden, was bereits im Code angegeben ist. Wenn Sie dasselbe zweimal sagen, wird es nicht korrekter.

Sie tun Schreibtests Überprüfung GetParityMitsprache zurück Evenfür 0, Odd1 und so weiter. Dies ist hilfreich, da Sie den Code nicht wiederholen, sondern das Verhalten des Codes unabhängig von der Implementierung überprüfen. Wenn der Code GetParitykomplett neu geschrieben würde, wären die Tests immer noch gültig. Der Hauptvorteil von Komponententests besteht darin, dass Sie Code sicher umschreiben und umgestalten können, indem Sie sicherstellen, dass der Code weiterhin wie erwartet funktioniert.

Wenn Sie jedoch einen Test haben, der sicherstellt, dass eine Enum- Deklaration die erwarteten Namen definiert, müssen Sie den Test auch ändern, wenn Sie in Zukunft Änderungen an der Enum-Liste vornehmen. Dies bedeutet, dass es nicht nur doppelt so viel Arbeit ist, sondern auch, dass der Nutzen für den Komponententest verloren geht. Wenn Sie Code und Test am ändern müssen gleichzeitig , dann gibt es keinen Schutz gegen die Einführung Bugs.

JacquesB
quelle
Ich habe meine Frage aktualisiert, um diese Antwort zu beantworten. Überprüfen Sie, ob dies hilfreich ist.
IS1_SO
@ IS1_SO: OK, das verwirrt mich - generieren Sie dynamisch die Aufzählungswerte, oder was passiert?
JacquesB
Nein. Ich meinte, dass in diesem Fall das zur Darstellung der Werte ausgewählte Sprachkonstrukt eine Aufzählung ist. Aber wie wir wissen, ist dies ein Implementierungsdetail. Was passiert, wenn man ein Array oder ein Set <> (in Java) oder einen String mit einigen Separationstoken zur Darstellung der Werte auswählt? In diesem Fall ist es sinnvoll zu prüfen, ob die enthaltenen Werte für das Unternehmen von Interesse sind. Das ist mein Punkt. Hilft diese Erklärung?
IS1_SO
3
@ IS1_SO: Sprechen Sie über das Testen einer von einer Funktion zurückgegebenen Aufzählungsinstanz mit einem bestimmten erwarteten Wert? Da könntest du ja das testen. Sie müssen nur die Enum-Deklaration selbst nicht testen.
JacquesB
11

Wenn das Risiko besteht, dass das Ändern der Aufzählung Ihren Code beschädigt, ist alles mit dem Attribut [Flags] in C # ein guter Fall, da das Hinzufügen eines Werts zwischen 2 und 4 (3) eine bitweise 1 und 2 anstelle von a wäre diskreter Artikel.

Es ist eine Schutzschicht.

Sie sollten in Betracht ziehen, einen Enum-Verhaltenskodex zu haben, mit dem alle Entwickler vertraut sind. Verlassen Sie sich nicht auf Textdarstellungen der Aufzählung, da diese häufig vorkommen. Dies kann jedoch zu Konflikten mit Ihren Serialisierungsrichtlinien führen.

Ich habe gesehen, wie Leute die Großschreibung von Aufzählungs-Einträgen "korrigiert" haben, sie alphabetisch oder nach einer anderen logischen Gruppierung sortiert haben, die alle andere fehlerhafte Code-Teile gebrochen hat.

Ian
quelle
5
Wenn die numerischen Werte der Aufzählung irgendwo verwendet werden, beispielsweise wenn sie in einer Datenbank gespeichert werden, kann eine Neuordnung (einschließlich Entfernen oder Einfügen vor dem letzten Wert) dazu führen, dass vorhandene Datensätze ungültig werden.
Stannius
3
+1, diese Antwort wird unterschätzt. Wenn Ihre Aufzählungen Teil der Serialisierung, Eingabeschnittstelle mit externem Wort oder bitweise zusammensetzbare Informationen sind, müssen sie auf jeden Fall bei jeder Version des Systems auf Konsistenz getestet werden. Zumindest, wenn Sie sich Gedanken über die Abwärtskompatibilität machen, was normalerweise eine gute Sache ist.
Machado
11

Nein, ein Test, bei dem überprüft wird, ob eine Aufzählung alle gültigen Werte enthält und nicht mehr, wiederholt im Wesentlichen die Deklaration der Aufzählung. Sie würden nur testen, ob die Sprache das Enum-Konstrukt ordnungsgemäß implementiert, was ein sinnloser Test ist.

Davon abgesehen sollten Sie das Verhalten testen, das von den Aufzählungswerten abhängt. Wenn Sie zum Beispiel die Aufzählungswerte verwenden, um Entitäten nach json oder was auch immer zu serialisieren, oder die Werte in einer Datenbank speichern, sollten Sie das Verhalten für alle Werte der Aufzählung testen. Auf diese Weise sollte, wenn die Aufzählung geändert wird, mindestens einer der Tests fehlschlagen. Was Sie auf jeden Fall testen würden, ist das Verhalten um Ihre Aufzählung, nicht die Aufzählung Deklaration selbst.

jesm00
quelle
3

Ihr Code sollte korrekt funktionieren, unabhängig von den tatsächlichen Werten einer Aufzählung. In diesem Fall sind keine Komponententests erforderlich.

Möglicherweise haben Sie jedoch Code, bei dem das Ändern eines Enum-Werts zu Problemen führt. Wenn beispielsweise ein Aufzählungswert in einer externen Datei gespeichert ist und nach dem Ändern des Aufzählungswerts das Lesen der externen Datei das falsche Ergebnis liefert. In diesem Fall wird in der Nähe der Aufzählung ein GROSSER Kommentar angezeigt, der jeden warnt, keine Werte zu ändern, und Sie können sehr gut einen Komponententest schreiben, der die numerischen Werte überprüft.

gnasher729
quelle
1

Im Allgemeinen ist es nicht besonders wertvoll, nur zu überprüfen, ob eine Aufzählung eine fest codierte Liste von Werten enthält, wie in anderen Antworten angegeben, da Sie dann nur Test und Aufzählung gemeinsam aktualisieren müssen.

Ich hatte einmal den Fall, dass ein Modul Aufzählungstypen von zwei anderen Modulen verwendete und zwischen ihnen abbildete. (Eine der Aufzählungen hatte zusätzliche Logik, die andere war für den DB-Zugriff, beide hatten Abhängigkeiten, die voneinander isoliert werden sollten.)

In diesem Fall habe ich einen Test (im Mapping-Modul) hinzugefügt, der überprüft hat, dass alle Enum-Einträge im Quell-Enum auch im Ziel-Enum vorhanden sind (und somit das Mapping immer funktionieren würde). (In einigen Fällen habe ich auch umgekehrt nachgesehen.)

Auf diese Weise schlug ein Test fehl, wenn jemand einen Listeneintrag zu einer der Listen hinzufügte und vergaß, den entsprechenden Eintrag zu der anderen hinzuzufügen.

Paŭlo Ebermann
quelle
1

Aufzählungen sind einfach endliche Typen mit benutzerdefinierten (hoffentlich aussagekräftigen) Namen. Eine Aufzählung kann nur einen Wert haben, voidder nur enthält null(einige Sprachen nennen dies unitund verwenden den Namen voidfür eine Aufzählung ohne Elemente!). Es kann zwei Werte haben, wie booldas hat falseund true. Es kann drei, wie colourChannelmit red, greenundblue . Und so weiter.

Wenn zwei Aufzählungen die gleiche Anzahl von Werten haben, sind sie "isomorph". dh wenn wir alle Namen systematisch austauschen, können wir einen anstelle des anderen verwenden und unser Programm wird sich nicht anders verhalten. Insbesondere verhalten sich unsere Tests nicht anders!

Zum Beispiel resultenthält win/ lose/ drawist isomorph zum oben colourChannel, da wir ersetzen kann zB colourChannelmit result, redmit win, greenmit loseund bluemit draw, und so lange , wie wir dies tun , überall (Erzeuger und Verbraucher, Parser und Serializer, Datenbankeinträge, Log - Dateien, usw. ) dann wird sich an unserem Programm nichts ändern. Alle " colourChannelTests", die wir geschrieben haben, werden immer noch bestehen, auch wenn es keine gibtcolourChannel keine mehr gibt!

Wenn eine Aufzählung mehr als einen Wert enthält, können wir diese Werte jederzeit neu anordnen , um eine neue Aufzählung mit der gleichen Anzahl von Werten zu erhalten. Da sich die Anzahl der Werte nicht geändert hat, ist die neue Anordnung gegenüber der alten isomorph, sodass wir alle Namen austauschen konnten und unsere Tests weiterhin bestanden würden (beachten Sie, dass dies nicht möglich ist die Definition einfach austauschen müssen) alle nutzungsseiten auch noch ausschalten).

Dies bedeutet, dass für die Maschine Aufzählungen "unterscheidbare Namen" und nichts anderes sind . Das einzige, was wir mit einer Enumeration machen können, ist zu verzweigen, ob zwei Werte gleich (zB red/ red) oder verschieden (zB red/ blue) sind. Das ist also das einzige, was ein "Unit-Test" tun kann, z

(  red == red  ) || throw TestFailure;
(green == green) || throw TestFailure;
( blue == blue ) || throw TestFailure;
(  red != green) || throw TestFailure;
(  red != blue ) || throw TestFailure;
...

Wie @ jesm00 sagt, ein solcher Test die prüft Sprache Implementierung anstatt das Programm. Diese Tests sind niemals eine gute Idee: Auch wenn Sie der Sprachimplementierung nicht vertrauen, sollten Sie sie von außen testen , da es nicht vertrauenswürdig ist, die Tests korrekt auszuführen!

Das ist also die Theorie; was ist mit der Praxis? Das Hauptproblem bei dieser Charakterisierung von Aufzählungen ist, dass Programme der „realen Welt“ selten in sich geschlossen sind: Wir verfügen über ältere Versionen, Remote- / Embedded-Bereitstellungen, historische Daten, Backups, Live-Datenbanken usw., sodass wir nie wirklich „ausschalten“ können. Alle Vorkommen eines Namens, ohne dass Verwendungen fehlen.

Solche Dinge liegen jedoch nicht in der „Verantwortung“ der Aufzählung selbst: Das Ändern einer Aufzählung kann die Kommunikation mit einem entfernten System unterbrechen, aber umgekehrt können wir ein solches Problem durch Ändern einer Aufzählung beheben !

In solchen Szenarien ist die Enum eine rot-Hering: was passiert , wenn ein System muss es sein , diese Art und Weise, und ein anderer muss es sein , dass Art und Weise? Es kann nicht beides sein, egal wie viele Tests wir schreiben! Der eigentliche Schuldige ist hier die Eingabe- / Ausgabeschnittstelle, die wohldefinierte Formate erzeugen / konsumieren sollte und nicht "welche Ganzzahl die Interpretationsauswahl auch immer". Die eigentliche Lösung besteht also darin , die E / A-Schnittstellen zu testen : mit Komponententests, um zu überprüfen, ob das erwartete Format analysiert / gedruckt wird, und mit Integrationstests, um zu überprüfen, ob das Format tatsächlich von der anderen Seite akzeptiert wird.

Wir mögen uns immer noch fragen, ob das Enum 'gründlich genug trainiert' wird, aber in diesem Fall ist das Enum wieder ein roter Hering. Was uns eigentlich Sorgen macht, ist die Testsuite selbst . Wir können hier auf verschiedene Arten Vertrauen gewinnen:

  • Die Codeabdeckung kann uns sagen, ob die Vielzahl der von der Testsuite stammenden Aufzählungswerte ausreicht, um die verschiedenen Verzweigungen im Code auszulösen. Wenn nicht, können wir Tests hinzufügen, die die nicht abgedeckten Zweige auslösen, oder eine größere Anzahl von Aufzählungen in den vorhandenen Tests generieren.
  • Die Eigenschaftsprüfung kann uns sagen, ob die Vielzahl der Verzweigungen im Code ausreicht, um die Laufzeitmöglichkeiten zu handhaben. Wenn zum Beispiel der Code nur verarbeitet redund wir nur mit testen red, haben wir eine 100% ige Abdeckung. Eine Eigenschaftsprüfung generiert (versucht) Gegenbeispiele zu unseren Behauptungen, wie z. B. das Generieren der Werte greenund blue, die wir vergessen haben, zu testen.
  • Mutationstests können uns sagen, ob unsere Behauptungen tatsächlich die Aufzählung überprüfen , anstatt nur den Zweigen zu folgen und ihre Unterschiede zu ignorieren.
Warbo
quelle
1

Unit Tests sind für das Testen von Units.

Bei der objektorientierten Programmierung ist eine Unit häufig eine gesamte Schnittstelle, z. B. eine Klasse, kann jedoch auch eine einzelne Methode sein.

https://en.wikipedia.org/wiki/Unit_testing

Ein automatisierter Test für eine deklarierte Enumeration würde eher die Integrität der Sprache und der Plattform, auf der sie ausgeführt wird, als die Logik in Code testen, der vom Entwickler erstellt wurde. Es würde keinen nützlichen Zweck erfüllen - Dokumentation eingeschlossen, da der Code, der die Aufzählung deklariert, ebenso als Dokumentation dient wie Code, der es testen würde.

Digimunk
quelle
0

Es wird erwartet, dass Sie das beobachtbare Verhalten Ihres Codes und die Auswirkungen von Methoden- / Funktionsaufrufen auf den beobachtbaren Status testen. Solange der Code das Richtige für Sie ist, müssen Sie nichts weiter testen.

Sie müssen nicht explizit behaupten, dass ein Aufzählungstyp die von Ihnen erwarteten Einträge enthält, genauso wie Sie nicht explizit behaupten, dass eine Klasse tatsächlich vorhanden ist oder die von Ihnen erwarteten Methoden und Attribute aufweist.

Tatsächlich wird durch das Testen des Verhaltens implizit bestätigt, dass die am Test beteiligten Klassen, Methoden und Werte vorhanden sind, sodass Sie es nicht explizit bestätigen müssen.

Beachten Sie, dass Sie keine aussagekräftigen Namen für Ihren Code benötigen, um das Richtige zu tun. Dies ist nur eine Annehmlichkeit für die Leser Ihres Codes. Sie könnten Ihren Code mit Aufzählungswerten wie foo, bar... und Methoden wie arbeiten lassen frobnicate().

Hör auf, Monica Schaden zuzufügen
quelle