Best Practice: Initialisieren Sie JUnit-Klassenfelder in setUp() oder bei der Deklaration?

Lesezeit: 8 Minuten

Benutzer-Avatar
Craig P. Motlin

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, letzte Felder zu verwenden. Wenn nicht brauchen Um die Methode setUp() für die Einrichtung zu verwenden, sollte ich sie dennoch verwenden und warum?

Klärung:
JUnit instanziiert die Testklasse einmal pro Testmethode. Das bedeutet list wird einmal pro Test erstellt, unabhängig davon, wo ich es deklariere. Außerdem gibt es keine zeitlichen Abhängigkeiten zwischen den Tests. Es scheint also, als gäbe es keine Vorteile bei der Verwendung von setUp(). Die JUnit-FAQ enthält jedoch viele Beispiele, die eine leere Sammlung in setUp() initialisieren, also denke ich, dass es einen Grund geben muss.

  • Beachten Sie, dass die Antwort in JUnit 4 (in der Deklaration initialisieren) und JUnit 3 (setUp verwenden) unterschiedlich ist; das ist die Wurzel der Verwirrung.

    – Nils von Barth

    24. April 2015 um 3:48 Uhr

  • Siehe auch stackoverflow.com/questions/6094081/…

    – Grigori Kislin

    17. März 2016 um 10:59 Uhr

Wenn Sie sich speziell über die Beispiele in der JUnit-FAQ wundern, wie z grundlegende Testvorlageich denke, die beste Methode, die dort gezeigt wird, ist, dass die Klasse im Test sollte in Ihrer setUp-Methode (oder in einer Testmethode) instanziiert werden.

Wenn die JUnit-Beispiele eine ArrayList in der Methode setUp erstellen, testen sie alle das Verhalten dieser ArrayList mit Fällen wie testIndexOutOfBoundException, testEmptyCollection und dergleichen. Die Perspektive dort ist, dass jemand einen Kurs schreibt und dafür sorgt, dass er 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 vernünftige Ausgabe erhalten, wenn Sie es später beschädigen.

Wenn Sie andererseits eine Java-Collection-Klasse (oder eine andere Bibliotheksklasse) in Ihrem Testcode verwenden, liegt das wahrscheinlich nicht daran, dass Sie sie testen möchten – sie ist nur ein Teil der Testvorrichtung. In diesem Fall können Sie sicher davon ausgehen, dass es wie beabsichtigt funktioniert, sodass die Initialisierung in der Deklaration kein Problem darstellt.

Für das, was es wert ist, arbeite ich an einer ziemlich großen, mehrere Jahre alten, TDD-entwickelten Codebasis. Wir initialisieren gewöhnlich Dinge in ihren Deklarationen im Testcode, und in den anderthalb Jahren, in denen ich an diesem Projekt arbeite, hat es nie ein Problem verursacht. Es gibt also zumindest einige anekdotische Beweise dafür, dass es eine vernünftige Sache ist.

Benutzer-Avatar
Craig P. Motlin

Ich fing an, selbst zu graben, und fand einen potenziellen Vorteil der Verwendung setUp(). Wenn während der Ausführung von Ausnahmen ausgelöst werden setUp(), JUnit druckt einen sehr hilfreichen Stack-Trace. Wenn andererseits während der Objektkonstruktion eine Ausnahme ausgelöst wird, besagt die Fehlermeldung einfach, 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 zum Erstellen einer leeren Sammlung, da das niemals geworfen wird, aber es ist ein Vorteil des setUp() Methode.

Benutzer-Avatar
Jürgen Hannaert

Zusätzlich zur Antwort von Alex B.

Es ist sogar erforderlich, die Methode setUp 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, würde jeder Teststatus gelöscht werden, nachdem einer ausgeführt wurde.

JUnit erstellt zuerst Instanzen der testClass für jede Testmethode und beginnt mit der Ausführung der Tests, nachdem jede Instanz erstellt wurde. Vor dem Ausführen des Testverfahrens wird dessen Setup-Verfahren ausgeführt, in dem ein gewisser 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 Tests mit einem Dirty-Zustand laufen.

JUnits-Lebenszyklus:

  1. Erstellen Sie für jede Testmethode eine andere Testklasseninstanz
  2. Wiederholen Sie dies für jede Testklasseninstanz: Rufen Sie setup auf + rufen Sie die Testmethode auf

Bei einigen Protokollierungen in einem Test mit zwei Testmethoden erhalten Sie: (Zahl ist der Hashcode)

  • Neue Instanz erstellen: 5718203
  • Neue Instanz erstellen: 5947506
  • Einrichtung: 5718203
  • TestOne: 5718203
  • Einrichtung: 5947506
  • TestZwei: 5947506

  • Richtig, aber Off-Topic. Die Datenbank ist im Wesentlichen ein globaler Zustand. Das ist für mich kein Problem. Mir geht es lediglich um die Ausführungsgeschwindigkeit richtig unabhängiger Tests.

    – Craig P. Motlin

    4. Februar 2009 um 17:26 Uhr

  • Diese Initialisierungsreihenfolge gilt nur in JUnit 3, wo es eine wichtige Warnung ist. In JUnit 4 werden Testinstanzen träge erstellt, sodass die Initialisierung in der Deklaration oder in einer Setup-Methode beide zur Testzeit erfolgt. Auch zur einmaligen Einrichtung kann man es nutzen @BeforeClass in JUnit 4.

    – Nils von Barth

    24. April 2015 um 3:45 Uhr


Benutzer-Avatar
Nils von Barth

In JUnit 4:

  • Für die Klasse im Testinitialisieren in a @Before Methode, um Fehler abzufangen.
  • Zum andere Klassenin der Deklaration initialisieren …
    • … der Kürze halber und um Felder zu markieren finalgenau wie in der Frage angegeben,
    • …es sei denn, es ist komplexe Initialisierung das könnte fehlschlagen, in diesem Fall verwenden @Beforeum Fehler abzufangen.
  • Zum globaler Staat (bes. langsame Initialisierungwie eine Datenbank), verwenden @BeforeClassaber vorsichtig sein von Abhängigkeiten zwischen Tests.
  • Initialisierung eines Objekts, das in a verwendet wird Einzeltest sollte natürlich im Prüfverfahren selbst erfolgen.

Initialisierung in a @Before Methode oder Testmethode ermöglicht Ihnen eine bessere Fehlerberichterstattung über Fehler. Dies ist besonders nützlich, um die zu testende Klasse zu instanziieren (die Sie möglicherweise beschädigen könnten), ist aber auch nützlich, um externe Systeme aufzurufen, z. B. den Zugriff auf das Dateisystem (“Datei nicht gefunden”) oder das Herstellen einer Verbindung zu einer Datenbank (“Verbindung abgelehnt”).

es ist akzeptabel einen einfachen Standard zu haben und immer zu verwenden @Before (deutliche Fehler, aber ausführlich) oder immer in der Deklaration initialisieren (präzise, ​​aber verwirrende Fehler), da komplexe Codierungsregeln schwer zu befolgen sind, und dies keine große Sache ist.

Initialisierung ein setUp ist ein Relikt von JUnit 3, wo alle Testinstanzen eifrig initialisiert wurden, was Probleme (Geschwindigkeit, Speicher, Ressourcenerschöpfung) verursacht, wenn Sie eine teure Initialisierung durchführen. Daher bestand die beste Vorgehensweise darin, eine teure Initialisierung durchzuführen setUp, die nur ausgeführt wurde, als der Test ausgeführt wurde. Dies gilt nicht mehr, sodass die Verwendung viel seltener erforderlich ist setUp.

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.

Benutzer-Avatar
dsaff

In JUnit 3 werden Ihre Feldinitialisierer einmal pro Testmethode ausgeführt bevor irgendwelche Tests durchgeführt werden. Solange Ihre Feldwerte klein im Speicher sind, wenig Einrichtungszeit benötigen und den globalen Zustand nicht beeinflussen, ist die Verwendung von Feldinitialisierern technisch in Ordnung. Wenn diese jedoch nicht gelten, kann es sein, dass Sie viel Speicher oder Zeit für die Einrichtung Ihrer Felder verbrauchen, bevor der erste Test ausgeführt wird, und möglicherweise sogar der Speicher ausgeht. Aus diesem Grund setzen viele Entwickler Feldwerte immer in der Methode setUp(), wo es immer sicher ist, auch wenn es nicht unbedingt notwendig ist.

Beachten Sie, dass in JUnit 4 die Testobjektinitialisierung direkt vor der Testausführung erfolgt, und daher ist die Verwendung von Feldinitialisierern sicherer und empfohlener Stil.

  • Interessant. Das eingangs beschriebene Verhalten gilt also nur für JUnit 3?

    – Craig P. Motlin

    2. Juni 2011 um 16:13 Uhr

Benutzer-Avatar
zustimmen

In Ihrem Fall (Erstellen einer Liste) gibt es in der Praxis keinen Unterschied. Aber im Allgemeinen ist es besser, setUp() zu verwenden, da dies Junit hilft, Ausnahmen korrekt zu melden. Wenn im Konstruktor/Initialisierer eines Tests eine Ausnahme auftritt, handelt es sich um einen Test Versagen. Wenn jedoch während des Setups eine Ausnahme auftritt, ist es normal, dies als ein Problem beim Einrichten des Tests zu betrachten, und junit meldet dies entsprechend.

  • Interessant. Das eingangs beschriebene Verhalten gilt also nur für JUnit 3?

    – Craig P. Motlin

    2. Juni 2011 um 16:13 Uhr

Ich bevorzuge zuerst die Lesbarkeit, die meistens nicht die Setup-Methode verwendet. Ich mache eine Ausnahme, wenn ein grundlegender Einrichtungsvorgang lange dauert und innerhalb jedes Tests wiederholt wird.
An diesem Punkt verschiebe ich diese Funktionalität in eine Setup-Methode mit der @BeforeClass Anmerkung (später optimieren).

Optimierungsbeispiel mit der @BeforeClass Setup-Methode: Ich verwende dbunit für einige Datenbank-Funktionstests. Die Setup-Methode ist dafür verantwortlich, die Datenbank in einen bekannten Zustand zu versetzen (sehr langsam … 30 Sekunden – 2 Minuten, je nach Datenmenge). Diese Daten lade ich in der mit kommentierten Setup-Methode @BeforeClass und führen Sie dann 10-20 Tests mit demselben Datensatz durch, anstatt die Datenbank in jedem Test neu zu laden/initialisieren.

Die Verwendung von Junit 3.8 (Erweitern von TestCase wie in Ihrem Beispiel gezeigt) erfordert das Schreiben von etwas mehr Code als nur das Hinzufügen einer Anmerkung, aber die “einmalige Ausführung vor der Klasseneinrichtung” ist weiterhin möglich.

  • +1, weil ich auch die Lesbarkeit bevorzuge. Ich bin jedoch nicht davon überzeugt, dass der zweite Weg überhaupt eine Optimierung ist.

    – Craig P. Motlin

    4. Februar 2009 um 16:55 Uhr

  • @Motlin Ich habe das dbunit-Beispiel hinzugefügt, um zu verdeutlichen, wie Sie mit dem Setup optimieren können.

    – Alex B

    4. Februar 2009 um 17:05 Uhr

  • Die Datenbank ist im Wesentlichen ein globaler Zustand. Das Verschieben des db-Setups nach setUp() ist also keine Optimierung, es ist notwendig, damit die Tests ordnungsgemäß abgeschlossen werden.

    – Craig P. Motlin

    4. Februar 2009 um 17:28 Uhr

  • @Alex B: Wie Motlin sagte, ist dies keine Optimierung. Sie ändern nur, wo im Code die Initialisierung durchgeführt wird, aber nicht wie oft oder wie schnell.

    – Edi

    8. Februar 2009 um 0:39 Uhr

  • Ich wollte die Verwendung der Annotation “@BeforeClass” implizieren. Bearbeiten Sie das Beispiel zur Verdeutlichung.

    – Alex B

    8. Februar 2009 um 14:34 Uhr

1333960cookie-checkBest Practice: Initialisieren Sie JUnit-Klassenfelder in setUp() oder bei der Deklaration?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy