it-swarm.com.de

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

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 bin der Meinung, dass das, 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 geschäftlicher Sicht haben muss, nicht mit dem Typsystem getestet werden können, sollte es meines Erachtens einen Komponententest dafür geben.

Diese Frage ist nicht ähnlich zu diese , da sie nicht das gleiche Problem wie meine anspricht. In dieser Frage gibt es eine Geschäftsfunktion (savePeople) und die Person erkundigt sich nach der internen Implementierung (forEach). Dort befindet sich eine mittlere Geschäftsschicht (die Funktion Personen speichern), die das Sprachkonstrukt (forEach) kapselt. Hier wird das Sprachkonstrukt (enum) verwendet, um das Verhalten aus geschäftlicher Sicht zu spezifizieren.

In diesem Fall stimmt das Implementierungsdetail mit der "wahren Natur" der Daten überein, dh: einer Menge (im mathematischen Sinne) von Werten. Sie könnten möglicherweise eine unveränderliche Menge verwenden, aber die gleichen Werte sollten dort immer noch vorhanden sein. Wenn Sie ein Array verwenden, muss dasselbe getan werden, 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

16
IS1_SO

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 Staat.

Grundsätzlich ist die Tatsache, dass Sie eine Aufzählung verwenden, ein Implementierungsdetail; Das ist die Art von Dingen, die Sie möglicherweise in ein anderes Design umgestalten möchten.

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

Es ist jedoch eine gute Idee, das von den Aufzählungen unterstützte Verhalten zu testen. Mit anderen Worten, wenn Sie von einer bestandenen Testsuite ausgehen und einen einzelnen Aufzählungswert auskommentieren, sollte mindestens ein Test fehlschlagen (Kompilierungsfehler werden als Fehler betrachtet).

40
VoiceOfUnreason

Sie testen keine Aufzählungsdeklaration . Sie können testen, ob die Funktionseingabe/-ausgabe die erwarteten Aufzählungswerte aufweist. Beispiel:

enum Parity {
    Even,
    Odd
}

Parity GetParity(int x) { ... }

Sie schreiben keine Tests, um zu überprüfen, ob enum Parity die Namen Even und Odd definiert . Ein solcher Test wäre sinnlos, da Sie nur das wiederholen würden, was bereits im Code angegeben ist. Dasselbe zweimal zu sagen, macht es nicht korrekter.

Sie schreiben Tests, um zu überprüfen, ob GetParity say Even für 0, Odd für 1 und zurückgibt bald. Dies ist nützlich, da Sie den Code nicht wiederholen, sondern das Verhalten des Codes unabhängig von der Implementierung überprüfen. Wenn der Code in GetParity vollständig neu geschrieben würde, wären die Tests weiterhin gültig. In der Tat besteht der Hauptvorteil von Komponententests darin, dass Sie den Code sicher umschreiben und umgestalten können, indem sichergestellt wird, dass der Code weiterhin wie erwartet funktioniert.

Wenn Sie jedoch einen Test haben, der sicherstellt, dass eine Aufzählungsdeklaration die erwarteten Namen definiert, müssen Sie jede Änderung, die Sie in Zukunft an der Aufzählung vornehmen, ändern der Test auch. Dies bedeutet, dass es nicht nur doppelt so viel Arbeit ist, sondern auch, dass jeglicher Nutzen für den Komponententest verloren geht. Wenn Sie den Code ändern und gleichzeitig testen müssen , gibt es keinen Schutz gegen das Einführen von Fehlern.

17
JacquesB

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 ist diskreter Gegenstand.

Es ist eine Schutzschicht.

Sie sollten einen Enum-Verhaltenskodex in Betracht ziehen, mit dem alle Entwickler vertraut sind. Verlassen Sie sich nicht auf Textdarstellungen der Aufzählung, da dies häufig vorkommt. Dies kann jedoch im Widerspruch zu Ihren Serialisierungsrichtlinien stehen.

Ich habe gesehen, wie Leute die Großschreibung von Aufzählungseinträgen "korrigierten", sie alphabetisch sortierten oder nach einer anderen logischen Gruppierung, die alle andere Teile schlechten Codes brachen.

11
Ian

Nein, ein Test, der überprüft, ob eine Aufzählung alle gültigen Werte enthält und nichts weiter, 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 Enum-Werten abhängt. Wenn Sie beispielsweise die Aufzählungswerte verwenden, um Entitäten in 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 mindestens einer der Tests fehlschlagen, wenn die Aufzählung geändert wird. In jedem Fall würden Sie das Verhalten um Ihre Aufzählung testen, nicht die Aufzählungsdeklaration selbst.

11
jesm00

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

Möglicherweise haben Sie jedoch Code, mit dem das Ändern eines Aufzählungswerts 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 davor warnt, Werte zu ändern, und Sie können sehr gut einen Komponententest schreiben, der die numerischen Werte überprüft.

3
gnasher729

Im Allgemeinen ist es nicht sehr wertvoll, nur zu überprüfen, ob eine Aufzählung eine fest codierte Liste von Werten enthält, wie andere Antworten sagten, 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 zugeordnet wurde. (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 Zuordnungsmodul) hinzugefügt, mit dem überprüft wurde, ob alle Aufzählungseinträge in der Quellaufzählung auch in der Zielaufzählung vorhanden sind (und somit die Zuordnung immer funktionieren würde). (In einigen Fällen habe ich auch umgekehrt nachgesehen.)

Auf diese Weise schlug ein Test fehl, wenn jemand einen Enum-Eintrag zu einem der Enums hinzufügte und vergaß, den entsprechenden Eintrag zu dem anderen hinzuzufügen.

1
Paŭlo Ebermann

Aufzählungen sind einfach endliche Typen mit benutzerdefinierten (hoffentlich bedeutungsvollen) Namen. Eine Aufzählung hat möglicherweise nur einen Wert, wie void, der nur null enthält (einige Sprachen nennen dies unit und verwenden den Namen void für eine Aufzählung mit nein Elemente!). Es kann zwei Werte haben, wie bool mit false und true. Es kann drei haben, wie colourChannel mit red, green und blue. Und so weiter.

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

Zum Beispiel ist result, das win/lose/draw enthält, isomorph zu dem obigen colourChannel, da wir z.B. colourChannel mit result, red mit win, green mit lose und blue mit draw, und solange wir dies tun überall (Produzenten und Konsumenten, Parser und Serialisierer, Datenbankeinträge, Protokolldateien usw.), wird sich an unserem Programm nichts ändern. Alle "colourChannel Tests", die wir geschrieben haben, bestehen weiterhin, obwohl es keine colourChannel mehr gibt!

Wenn eine Aufzählung mehr als einen Wert enthält, können wir diese Werte immer 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 isomorph zur alten, und daher könnten wir alle Namen austauschen, und unsere Tests würden immer noch bestehen (beachten Sie, dass wir können nur Schalten Sie die Definition aus (wir müssen immer noch alle Verwendungsseiten ausschalten).

Dies bedeutet, dass Enums für die Maschine "unterscheidbare Namen" und sonst nichts sind. Das einzige, was wir mit einer Aufzählung tun können, ist zu verzweigen, ob zwei Werte gleich (z. B. red/red) oder unterschiedlich (z. B. red/blue). 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, überprüft ein solcher Test eher die Sprachimplementierung als Ihr Programm. Diese Tests sind niemals eine gute Idee: Auch wenn Sie der Sprachimplementierung nicht vertrauen, sollten Sie sie testen von außen, 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 "reale" Programme selten in sich geschlossen sind: Wir haben Legacy-Versionen, Remote-/Embedded-Bereitstellungen, historische Daten, Backups, Live-Datenbanken usw., sodass wir nie wirklich "ausschalten" können. alle Vorkommen eines Namens, ohne einige Verwendungen zu verpassen.

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 beheben ein solches Problem durch Ändern einer Aufzählung!

In solchen Szenarien ist die Aufzählung ein roter Hering: Was ist, wenn ein System es braucht dieser Weg, und ein anderes muss es sein das Weg? Es kann nicht beides sein, egal wie viele Tests wir schreiben! Der eigentliche Schuldige hier ist die Eingabe-/Ausgabeschnittstelle, die genau definierte Formate erzeugen/verbrauchen sollte und nicht "welche Ganzzahl auch immer die Interpretation auswählt". Die eigentliche Lösung besteht also darin, Testen der E/A-Schnittstellen: 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 die Aufzählung "gründlich genug ausgeübt" wird, aber in diesem Fall ist die Aufzählung wieder ein roter Hering. Was uns eigentlich Sorgen macht, ist die Testsuite selbst. Wir können hier auf verschiedene Weise Vertrauen gewinnen:

  • Die Codeabdeckung kann uns sagen, ob die verschiedenen Enum-Werte aus der Testsuite ausreichen, um die verschiedenen Zweige im Code auszulösen. Wenn nicht, können wir Tests hinzufügen, die die nicht abgedeckten Zweige auslösen, oder in den vorhandenen Tests eine größere Auswahl an Aufzählungen generieren.
  • Die Eigenschaftsprüfung kann uns sagen, ob die Vielzahl der Zweige im Code ausreicht, um die Laufzeitmöglichkeiten zu bewältigen. Wenn der Code beispielsweise nur red verarbeitet und wir nur mit red testen, haben wir eine 100% ige Abdeckung. Ein Eigenschaftsprüfer wird (versuchen) Gegenbeispiele zu unseren Behauptungen generieren, z. B. die Werte green und blue, die wir vergessen haben zu testen.
  • Mutationstests können uns sagen, ob unsere Behauptungen tatsächlich check die Aufzählung sind, anstatt nur den Zweigen zu folgen und ihre Unterschiede zu ignorieren.
1
Warbo

Nein. Unit-Tests dienen zum Testen von Einheiten.

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

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

Ein automatisierter Test für eine deklarierte Enumeration würde die Integrität der Sprache und der Plattform testen, auf der sie ausgeführt wird, und nicht die Logik des vom Entwickler erstellten Codes. Es würde keinen nützlichen Zweck erfüllen - Dokumentation enthalten, da der Code, der die Aufzählung deklariert, genauso als Dokumentation dient wie Code, der sie testen würde.

1
digimunk

Von Ihnen wird erwartet, dass Sie das beobachtbare Verhalten Ihres Codes und die Auswirkungen von Methoden-/Funktionsaufrufen auf den beobachtbaren Zustand testen. Solange der Code das Richtige tut, müssen Sie nichts anderes testen.

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

Indem Sie das Verhalten testen, behaupten Sie implizit, dass die am Test beteiligten Klassen, Methoden und Werte vorhanden sind, sodass Sie es nicht explizit behaupten 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 Leute, die Ihren Code lesen. Sie können Ihren Code mit Enum-Werten wie foo, bar... und Methoden wie frobnicate() arbeiten lassen.

0