Soll ich Klassenfelder bei einer solchen Deklaration initialisieren?
public class SomeTest extends TestCase
{
private final List list = new ArrayList();
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Oder in setUp () so?
public class SomeTest extends TestCase
{
private List list;
@Override
protected void setUp() throws Exception
{
super.setUp();
this.list = new ArrayList();
}
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Ich neige dazu, das erste Formular zu verwenden, weil es prägnanter ist und es mir ermöglicht, endgültige Felder zu verwenden. Wenn ich nicht brauchen , um den Setup () -Methode für Set-up zu verwenden, soll ich es immer noch verwenden, und warum?
Erläuterung:
JUnit instanziiert die Testklasse einmal pro Testmethode. Das bedeutet list
, dass einmal pro Test erstellt wird, unabhängig davon, wo ich es deklariere. Dies bedeutet auch, dass zwischen den Tests keine zeitlichen Abhängigkeiten bestehen. Die Verwendung von setUp () scheint also keine Vorteile zu haben. Die JUnit-FAQ enthält jedoch viele Beispiele, die eine leere Sammlung in setUp () initialisieren. Ich denke, es muss einen Grund geben.
Antworten:
Wenn Sie sich speziell über die Beispiele in den JUnit-FAQ wundern, wie z. B. die grundlegende Testvorlage , ist es meiner Meinung nach die beste Vorgehensweise, die getestete Klasse in Ihrer setUp-Methode (oder in einer Testmethode) zu instanziieren. .
Wenn die JUnit-Beispiele eine ArrayList in der setUp-Methode erstellen, testen sie alle das Verhalten dieser ArrayList mit Fällen wie testIndexOutOfBoundException, testEmptyCollection und dergleichen. Die Perspektive besteht darin, dass jemand eine Klasse schreibt und sicherstellt, dass sie richtig funktioniert.
Sie sollten wahrscheinlich dasselbe tun, wenn Sie Ihre eigenen Klassen testen: Erstellen Sie Ihr Objekt in setUp oder in einer Testmethode, damit Sie eine angemessene Ausgabe erhalten, wenn Sie es später brechen.
Wenn Sie dagegen eine Java-Auflistungsklasse (oder eine andere Bibliotheksklasse) in Ihrem Testcode verwenden, liegt dies wahrscheinlich nicht daran, dass Sie sie testen möchten, sondern nur daran, dass sie Teil des Testgeräts ist. In diesem Fall können Sie davon ausgehen, dass es wie beabsichtigt funktioniert, sodass das Initialisieren in der Deklaration kein Problem darstellt.
Für das, was es wert ist, arbeite ich an einer ziemlich großen, mehrere Jahre alten, von TDD entwickelten Codebasis. Wir initialisieren gewöhnlich Dinge in ihren Deklarationen im Testcode, und in den anderthalb Jahren, in denen ich an diesem Projekt teilgenommen habe, hat es nie ein Problem verursacht. Es gibt also zumindest einige anekdotische Beweise dafür, dass dies eine vernünftige Sache ist.
quelle
Ich fing an, mich selbst zu graben und fand einen möglichen Vorteil der Verwendung
setUp()
. Wenn während der Ausführung von Ausnahmen Ausnahmen ausgelöstsetUp()
werden, druckt JUnit eine sehr hilfreiche Stapelverfolgung. Wenn andererseits während der Objektkonstruktion eine Ausnahme ausgelöst wird, heißt es in der Fehlermeldung lediglich, dass JUnit den Testfall nicht instanziieren konnte, und Sie sehen nicht die Zeilennummer, in der der Fehler aufgetreten ist, wahrscheinlich weil JUnit Reflektion verwendet, um den Test zu instanziieren Klassen.Nichts davon gilt für das Beispiel des Erstellens einer leeren Sammlung, da dies niemals ausgelöst wird, aber es ist ein Vorteil der
setUp()
Methode.quelle
Neben der Antwort von Alex B.
Es ist sogar erforderlich, die setUp-Methode zu verwenden, um Ressourcen in einem bestimmten Zustand zu instanziieren. Dies im Konstruktor zu tun, ist nicht nur eine Frage des Timings, sondern aufgrund der Art und Weise, wie JUnit die Tests ausführt, wird jeder Teststatus nach dem Ausführen eines Tests gelöscht.
JUnit erstellt zunächst Instanzen der Testklasse für jede Testmethode und startet die Tests, nachdem jede Instanz erstellt wurde. Vor dem Ausführen der Testmethode wird die Setup-Methode ausgeführt, in der ein Zustand vorbereitet werden kann.
Wenn der Datenbankstatus im Konstruktor erstellt würde, würden alle Instanzen den Datenbankstatus direkt nacheinander instanziieren, bevor die einzelnen Tests ausgeführt werden. Ab dem zweiten Test würden die Tests mit einem verschmutzten Zustand ausgeführt.
JUnits Lebenszyklus:
Bei einigen Protokollierungen in einem Test mit zwei Testmethoden erhalten Sie: (Nummer ist der Hashcode)
quelle
@BeforeClass
in JUnit 4 verwenden.In Einheit 4:
@Before
Methode, um Fehler zu erkennen.final
, genau wie in der Frage angegeben,@Before
können Fehler erkannt werden.@BeforeClass
, aber seien Sie vorsichtig von Abhängigkeiten zwischen den Tests.Durch die Initialisierung einer
@Before
Methode oder Testmethode erhalten Sie eine bessere Fehlerberichterstattung bei Fehlern. Dies ist besonders nützlich, um die zu testende Klasse zu instanziieren (die möglicherweise unterbrochen wird), aber auch, um externe Systeme aufzurufen, z. B. den Zugriff auf das Dateisystem ("Datei nicht gefunden") oder eine Verbindung zu einer Datenbank herzustellen ("Verbindung abgelehnt").Es ist akzeptabel , einen einfachen Standard zu haben und immer zu verwenden
@Before
(klare Fehler, aber ausführlich) oder immer in der Deklaration zu initialisieren (prägnant, aber verwirrende Fehler), da komplexe Codierungsregeln schwer zu befolgen sind und dies keine große Sache ist.Das Initialisieren in
setUp
ist ein Relikt von JUnit 3, in dem alle Testinstanzen eifrig initialisiert wurden. Dies führt zu Problemen (Geschwindigkeit, Speicher, Erschöpfung der Ressourcen), wenn Sie eine teure Initialisierung durchführen. Die beste Vorgehensweise bestand daher darin, eine teure InitialisierungsetUp
durchzuführen, die nur ausgeführt wurde, wenn der Test ausgeführt wurde. Dies gilt nicht mehr, so dass die Verwendung viel weniger notwendig istsetUp
.Dies fasst mehrere andere Antworten zusammen, die die Lede begraben, insbesondere von Craig P. Motlin (Frage selbst und Selbstantwort), Moss Collum (Klasse im Test) und dsaff.
quelle
In JUnit 3 werden Ihre Feldinitialisierer einmal pro Testmethode ausgeführt, bevor Tests ausgeführt werden . Solange Ihre Feldwerte im Speicher klein sind, wenig Einrichtungszeit benötigen und keinen Einfluss auf den globalen Status haben, ist die Verwendung von Feldinitialisierern technisch in Ordnung. Wenn diese jedoch nicht zutreffen, verbrauchen Sie möglicherweise viel Speicher oder Zeit beim Einrichten Ihrer Felder, bevor der erste Test ausgeführt wird, und möglicherweise sogar nicht genügend Speicher. Aus diesem Grund legen viele Entwickler Feldwerte immer in der setUp () -Methode fest, wo dies immer sicher ist, auch wenn dies nicht unbedingt erforderlich ist.
Beachten Sie, dass in JUnit 4 die Initialisierung von Testobjekten unmittelbar vor dem Ausführen des Tests erfolgt. Daher ist die Verwendung von Feldinitialisierern sicherer und wird empfohlen.
quelle
In Ihrem Fall (Erstellen einer Liste) gibt es in der Praxis keinen Unterschied. Im Allgemeinen ist es jedoch besser, setUp () zu verwenden, da dies Junit dabei hilft, Ausnahmen korrekt zu melden. Wenn im Konstruktor / Initialisierer eines Tests eine Ausnahme auftritt, ist dies ein Testfehler . Wenn jedoch während des Setups eine Ausnahme auftritt, ist es naheliegend, diese als Problem beim Einrichten des Tests zu betrachten, und junit meldet sie entsprechend.
quelle
Ich bevorzuge zuerst die Lesbarkeit, bei der die Setup-Methode meistens nicht verwendet wird. Ich mache eine Ausnahme, wenn ein grundlegender Einrichtungsvorgang lange dauert und bei jedem Test wiederholt wird.
An diesem Punkt verschiebe ich diese Funktionalität mithilfe der
@BeforeClass
Anmerkung in eine Setup-Methode (später optimieren).Beispiel für die Optimierung mit der
@BeforeClass
Setup-Methode: Ich verwende dbunit für einige Datenbankfunktionstests. Die Setup-Methode ist dafür verantwortlich, die Datenbank in einen bekannten Zustand zu versetzen (sehr langsam ... 30 Sekunden - 2 Minuten, abhängig von der Datenmenge). Ich lade diese Daten in die mit kommentierte Setup-Methode und führe@BeforeClass
dann 10 bis 20 Tests mit demselben Datensatz aus, anstatt die Datenbank in jedem Test neu zu laden / zu initialisieren.Die Verwendung von Junit 3.8 (Erweiterung von TestCase wie in Ihrem Beispiel gezeigt) erfordert das Schreiben von etwas mehr Code als nur das Hinzufügen einer Anmerkung, aber das "Einmal vor dem Einrichten der Klasse ausführen" ist weiterhin möglich.
quelle
Da jeder Test unabhängig mit einer neuen Instanz des Objekts ausgeführt wird, macht es keinen Sinn, dass das
setUp()
Testobjekt einen internen Status hat, außer dem, der zwischen einem einzelnen Test und geteilt wirdtearDown()
. Dies ist ein Grund (zusätzlich zu den Gründen, die andere angegeben haben), dass es gut ist, diesetUp()
Methode zu verwenden.Hinweis: Es ist eine schlechte Idee für ein JUnit-Testobjekt, den statischen Status beizubehalten! Wenn Sie statische Variablen in Ihren Tests für andere Zwecke als für Tracking- oder Diagnosezwecke verwenden, machen Sie einen Teil des Zwecks von JUnit ungültig, dh, die Tests können (und können) in beliebiger Reihenfolge ausgeführt werden, wobei jeder Test mit a ausgeführt wird frischer, sauberer Zustand.
Die Verwendung hat den Vorteil,
setUp()
dass Sie nicht bei jeder Testmethode Initialisierungscode ausschneiden und einfügen müssen und dass Sie keinen Test-Setup-Code im Konstruktor haben. In Ihrem Fall gibt es kaum einen Unterschied. Das Erstellen einer leeren Liste kann sicher erfolgen, während Sie sie anzeigen, oder im Konstruktor, da es sich um eine triviale Initialisierung handelt. Wie Sie und andere bereits betont haben,Exception
sollte jedoch alles getan werden, was möglicherweise einen auslösen kann,setUp()
damit Sie den Diagnosestapel-Dump erhalten, wenn er fehlschlägt.In Ihrem Fall, in dem Sie nur eine leere Liste erstellen, würde ich genauso vorgehen, wie Sie es vorschlagen: Weisen Sie die neue Liste am Deklarationspunkt zu. Vor allem, weil Sie auf diese Weise die Möglichkeit haben, es zu markieren,
final
wenn dies für Ihre Testklasse sinnvoll ist.quelle
final
wird jedoch in der Frage erwähnt.Die konstanten Werte (Verwendung in Fixtures oder Assertions) sollten in ihren Deklarationen initialisiert werden und
final
(wie nie ändern)Das zu testende Objekt sollte in der Setup-Methode initialisiert werden, da wir möglicherweise Dinge einstellen. Natürlich können wir jetzt vielleicht nichts einstellen, aber wir könnten es später einstellen. Das Instanziieren in der init-Methode würde die Änderungen erleichtern.
Abhängigkeiten des zu testenden Objekts, wenn diese verspottet werden, sollten nicht einmal von Ihnen selbst instanziiert werden: Heute können die Schein-Frameworks sie durch Reflexion instanziieren.
Ein Test ohne Abhängigkeit zum Verspotten könnte folgendermaßen aussehen:
Ein Test mit zu isolierenden Abhängigkeiten könnte folgendermaßen aussehen:
quelle