Wann verwende ich das TestFixtureSetUp-Attribut anstelle eines Standardkonstruktors?

74

In der NUnit-Dokumentation ist nicht angegeben, wann eine Methode mit a verwendet werden soll TestFixtureSetupund wann das Setup im Konstruktor durchgeführt werden soll.

public class MyTest
{
    private MyClass myClass;

    public MyTest()
    {
        myClass = new MyClass();
    }

    [TestFixtureSetUp]
    public void Init()
    {
        myClass = new MyClass();
    }
}

Gibt es gute / schlechte Praktiken bezüglich des TestFixtureSetupVersus gegenüber dem Standardkonstruktor oder gibt es keinen Unterschied?

Paco
quelle
Da dies immer eine häufig gestellte Frage ist, sollten Sie dies und das berücksichtigen (die gleichen Diskussionen für MSTest). Achten Sie jedoch auf die Nebenwirkungen von Gerätekonfigurationen auf die Testlesbarkeit (siehe hier und hier ).
Massood Khaari
Nur mein 1 Cent: Dies ist nützlich, wenn Sie eine Basisklasse für viele Tests haben. Abgeleitete Klassen führen automatisch das in der übergeordneten Klasse definierte TestFixtureSetup aus. Sie können dies nicht mit Konstruktoren tun, es sei denn, Sie rufen sie speziell auf (dies kann ein Problem sein oder auch nicht, abhängig davon, wie viele abgeleitete Klassen Sie haben)
Nelson Rodriguez

Antworten:

17

Ich denke, dies war eines der Probleme, die vom nUnit-Team nicht angesprochen wurden. Es gibt jedoch das hervorragende xUnit-Projekt , bei dem genau dieses Problem festgestellt wurde und bei dem entschieden wurde, dass Konstruktoren eine gute Sache für die Initialisierung von Testvorrichtungen sind .

Für nunit, meine beste Praxis in diesem Fall war , die verwendet werden TestFixtureSetUp, TestFixtureTearDown, SetUpund TearDownMethoden wie in der Dokumentation beschrieben.

Ich denke, es hilft mir auch, wenn ich mir ein nUnit-Testgerät nicht als normale Klasse vorstelle, obwohl Sie es mit diesem Konstrukt definieren. Ich betrachte sie als Spielpaarungen, und das bringt mich über die mentale Hürde und ermöglicht mir, dieses Problem zu übersehen.

casademora
quelle
2
Bitte beachten Sie, dass der xUnit-Artikel darauf hinweist , dass Setups (sowie Konstruktoren) im Allgemeinen eine schlechte Idee sind, die es schwierig macht , Testcode zu befolgen , NICHT, dass Konstruktoren besser sind als Setups. Parameterlose Konstruktoren sind jedoch das letzte Mittel für diejenigen, die es wirklich brauchen. Siehe auch: Teststile und Vermeidung von Setup / Teardown
Massood Khaari
64

Warum sollten Sie in Ihren Testklassen einen Konstruktor verwenden müssen?

Ich verwende [SetUp]und [TearDown]markiere Methoden für Code, der vor und nach jedem Test ausgeführt werden soll, und ähnliche [TestFixtureSetUp]und [TestFixtureTearDown]markierte Methoden für Code, der nur einmal ausgeführt werden soll, bevor und nachdem alle Tests im Fixture ausgeführt wurden.

Ich denke, Sie könnten wahrscheinlich den [TestFixtureSetUp]Konstruktor durch einen ersetzen (obwohl ich es nicht versucht habe), aber dies scheint nur von der klaren Konvention zu brechen, die die markierten Methoden bieten.

Sam Wessel
quelle
4
Ich hatte keine Ahnung, warum ich einen Konstruktor brauche, aber ich habe auch keine Ahnung, warum ich ein TestFixtureSetUp brauche. Ich kenne die Attribute Setup, Teardown und Testfixtureteardown. Ich kenne den Unterschied zwischen dem Konstruktor- und dem Testfixturesetup-Attribut einfach nicht.
Paco
3
Nur um deine Frage zu beantworten. Sie müssen in Ihren Testklassen einen Konstruktor verwenden, wenn Sie Testvorrichtungen parametrisiert haben.
oderibas
12

Eine Sache [TestFixtureSetup], die Sie im Konstruktor nicht tun können, ist das Empfangen von Parametern von der [TestFixture].

Wenn Sie Ihr Testgerät parametrisieren möchten, müssen Sie den Konstruktor für mindestens einen Teil des Setups verwenden. Bisher habe ich dies nur für Integrationstests verwendet, z. B. zum Testen einer Datenzugriffsschicht mit mehreren Datenanbietern:

[TestFixture("System.Data.SqlClient",
  "Server=(local)\\SQLEXPRESS;Initial Catalog=MyTestDatabase;Integrated Security=True;Pooling=False"))]
[TestFixture("System.Data.SQLite", "Data Source=MyTestDatabase.s3db")])]
internal class MyDataAccessLayerIntegrationTests
{
    MyDataAccessLayerIntegrationTests(
        string dataProvider,
        string connectionString)
    {
        ...
    }
}
Ergwun
quelle
10

Es gibt einen Unterschied zwischen Konstruktor und Methode, die mit [TestFixtureSetUp]Attribut markiert sind . Laut NUnit-Dokumentation:

Es ist ratsam, dass der Konstruktor keine Nebenwirkungen hat, da NUnit das Objekt im Verlauf einer Sitzung möglicherweise mehrmals erstellen kann.

Wenn Sie also eine teure Initialisierung haben, ist es besser, diese zu verwenden TestFixtureSetUp.

oderibas
quelle
1
Dies setzt voraus, dass die mit TestFixtureSetUp gekennzeichnete Methode statisch ist. Andernfalls könnte der Konstruktor nicht häufiger aufgerufen werden, wenn die Testinstanz nicht weggeworfen wird, ohne von NUnit verwendet zu werden, was ich bezweifeln würde.
Jpierson
9

Ich habe mich oft gefragt, was nötig [TestFixtureSetUp]ist, da es ein einfaches, gut verstandenes erstklassiges Sprachkonstrukt gibt, das genau das Gleiche tut.

Ich bevorzuge die Verwendung von Konstruktoren, um das schreibgeschützte Schlüsselwort zu nutzen und sicherzustellen, dass Mitgliedsvariablen nicht neu initialisiert werden können.

Richard Ev
quelle
4
Das schreibgeschützte Schlüsselwort stellt nicht sicher, dass ein Mitglied nicht geändert wird. Wenn das Mitglied eine Klasse ist, kann der interne Status der Klasse in einem Test geändert werden, und dieser Status wird auf die nächste Testmethode übertragen (da nunit die Klasse zwischen den einzelnen Testläufen nicht neu erstellt)
Pete
4

[TestFixtureSetUp]und [TestFixtureTearDown]sind für die gesamte Testklasse. läuft nur einmal.

[SetUp]und [TearDown]sind für jede Testmethode (Test). läuft für jeden Test.

Shankar S.
quelle
3

Ein wichtiger Unterschied zwischen Konstruktor und TestFixtureSetUp besteht darin, dass zumindest in NUnit 2 der Konstruktorcode tatsächlich bei der Testaufzählung ausgeführt wird und nicht nur bei der Testausführung. Sie möchten also den Ctor-Code darauf beschränken, nur schreibgeschützte, dh Parameterwerte zu füllen. Alles, was Nebenwirkungen verursacht oder tatsächliche Arbeit leistet, muss entweder in Lazy verpackt oder in TestFixtureSetUp / OneTimeSetUp ausgeführt werden. Sie können sich den Konstruktor also nur als einen Ort zum Konfigurieren des Tests vorstellen. Während im TestFixtureSetUp das Testgerät installiert ist, wird der erforderliche Anfangszustand des Systems vor dem Ausführen der Tests initialisiert.

Novaterata
quelle
Es ist ein guter Punkt, nicht sicher, wie relevant heutzutage, aber eine Sache zu berücksichtigen.
Aiodintsov
2

Ich glaube, ich habe eine negative gute Antwort - der Grund, einen Konstruktor anstelle des Attributs zu verwenden, ist, wenn Sie eine Vererbung zwischen Testklassen haben.

Es wird nur eine mit Annotationen versehene Methode [TestFixtureSetup]aufgerufen (nur für die konkrete Klasse), die anderen Fixture-Initialisierer jedoch nicht. In diesem Fall würde ich die Initialisierung lieber in den Konstruktor einfügen, der eine genau definierte Semantik für die Vererbung hat :)

ripper234
quelle
2
Dies hat sich seit NUnit 2.5 geändert. Nun werden alle mit [TestFixtureSetup] kommentierten Methoden aufgerufen.
oderibas
-2

Der Konstruktor und die SetUpMethoden werden unterschiedlich verwendet:
Der Konstruktor wird nur einmal ausgeführt.
Die SetUpMethoden werden jedoch mehrmals ausgeführt, bevor jeder Testfall ausgeführt wird.

nicht existierender Mythos
quelle
1
Sie beziehen sich auf [SetUp], während sich das OP bezieht [TestFixtureSetUp].
Tag, wenn