2012-03-25 14 views
15

Betrachten Sie das folgende Beispiel eines Komponententests. Die Kommentare erklären mein Problem ziemlich gut.Immer nach einem Test aufräumen?

[TestMethod] 
public void MyTestMethod() 
{ 

    //generate some objects in the database 
    ... 

    //make an assert that fails sometimes (for example purposes, this fails always) 
    Assert.IsTrue(false); 

    //TODO: how do we clean up the data generated in the database now that the test has ended here? 

} 
+0

Sie sollten auch die "TearDown" (oder die entsprechende Entsprechung in Ihrer Suite) verwenden, um zu bereinigen, denn wenn Ihr Test fehlschlägt, wird der Bereinigungscode nicht ausgeführt. –

+0

Wie wäre es mit einer Ausnahme, die nach dem Aufräumen erneut geworfen wird? –

+0

Jimmy Bogard (Autor von * AutoMapper *) hat einen tollen Artikel und ein Tool: [Zuverlässige Datenbank Tests mit Respawn] (https://losechies.com/jimmybogard/2015/02/19/reliable-database-tests-with-respawn /) –

Antwort

24

Es gibt zwei Möglichkeiten, dies zu tun. One verwendet TestInitialize- und TestCleanup-Attribute für Methoden in der Testklasse. Sie werden immer vor und nach dem Test ausgeführt.

Eine andere Möglichkeit besteht darin, die Tatsache zu verwenden, dass Testfehler über Ausnahmen an den Test-Runner weitergegeben werden. Dies bedeutet, dass ein try {} finally {} Block in Ihrem Test verwendet werden kann, um alles zu bereinigen, nachdem eine Asserts fehlgeschlagen ist.

Die versuchen/endlich Bereinigung kann wirklich chaotisch werden, gibt es eine Menge von Objekten zu bereinigen. Was mein Team vorhat, ist eine Helferklasse, die IDisposable implementiert. Es verfolgt, welche Objekte erstellt wurden und schiebt sie auf einen Stapel. Wenn Dispose aufgerufen wird, werden die Elemente vom Stapel entfernt und aus der Datenbank entfernt.

[TestMethod] 
public void FooTest() 
{ 
    using (FooBarDatabaseContext context = new FooBarDatabaseContext()) 
    { 
    // setup some db objects. 
    Foo foo = context.NewFoo(); 
    Bar bar = context.NewBar(foo); 
    Assert.Fail(); 
    } // calls dispose. deletes bar, then foo. 
} 

Dies hat den zusätzlichen Vorteil, die Konstruktoren in Methodenaufrufe einzubinden. Wenn sich Konstruktorsignaturen ändern, können wir den Testcode leicht modifizieren.

+9

Ich liebe es zu picken; TestCleanup wird nicht ausgeführt, wenn TestInitialize eine Ausnahme ausgelöst hat. –

+4

@ carlin.scott Dein Nickerchen hat mir gerade den Tag gerettet –

6

Ein paar Antworten:

  1. Wenn es eine aktuelle Datenbank ist mit, ich würde behaupten, dass es im wahrsten Sinne des Wortes nicht ein „Unit-Test“ ist. Es ist ein Integrationstest. Ein Komponententest sollte solche Nebenwirkungen nicht haben. Erwägen Sie die Verwendung einer Mocking-Bibliothek, um die tatsächliche Datenbank zu simulieren. Rhino Mocks ist eins, aber es gibt viele andere.

  2. Wenn jedoch der ganze Punkt dieses Tests tatsächlich mit einer Datenbank interagieren soll, dann möchten Sie mit einer transienten test-only-Datenbank interagieren. In diesem Fall würde ein Teil Ihrer automatisierten Tests Code enthalten, um die Testdatenbank von Grund auf neu zu erstellen, dann führen Sie die Tests durch und zerstören dann die Testdatenbank. Die Idee ist, keine externen Nebenwirkungen zu haben. Es gibt wahrscheinlich mehrere Möglichkeiten, dies zu tun, und ich bin nicht vertraut genug mit Unit-Test-Frameworks, um wirklich einen konkreten Vorschlag zu geben. Aber wenn Sie die Tests verwenden, die in Visual Studio integriert sind, dann wäre vielleicht eine Visual Studio Database Project von Nutzen.

1

Ihre Frage ist ein bisschen zu allgemein. Normalerweise sollten Sie nach jedem einzelnen Test aufräumen. Normalerweise können Sie sich nicht darauf verlassen, dass alle Tests immer in der gleichen Reihenfolge ausgeführt werden und Sie sich sicher sein müssen, was sich in Ihrer Datenbank befindet. Für die allgemeine Einrichtung oder Bereinigung bieten die meisten Unit-Test-Frameworks die Methoden setUp und tearDown, die Sie überschreiben können und die automatisch aufgerufen werden. Ich weiß nicht, wie das in C# funktioniert, aber e. G. In JUnit (Java) haben Sie diese Methoden.

Ich stimme David zu. Ihre Tests sollten normalerweise keine Nebenwirkungen haben. Sie sollten für jeden einzelnen Test eine neue Datenbank einrichten.

0

Sie müssen in diesem Fall eine manuelle Bereinigung vornehmen. Dh, das Gegenteil von dem generieren einige Objekte in der Datenbank.

Die Alternative ist Mocking Tools wie Rhino Mocks zu verwenden, so dass die Datenbank nur eine In-Memory-Datenbank ist

0

Es hängt davon ab, was Sie sind tatsächlich Tests. Mit Blick auf die Kommentare würde ich sagen ja, aber nebenbei ist es schwierig abzuziehen auf Kommentare zu schauen. Wenn Sie das gerade eingefügte Objekt bereinigen, setzen Sie in der Praxis den Status des Tests zurück. Also, wenn Sie aufräumen, beginnen Sie, von Aufräumsystem zu testen.

10

Ich denke, die beste Antwort in Situationen wie dieser ist, sehr genau darüber nachzudenken, was Sie versuchen zu testen. Idealerweise sollte ein Komponententest versuchen, eine einzelne Tatsache über eine einzelne Methode oder Funktion zu testen. Wenn Sie anfangen, viele Dinge miteinander zu kombinieren, geht es in die Welt der Integrationstests über (die gleichermaßen wertvoll, aber unterschiedlich sind).

Für Unit-Testzwecke müssen Sie für die Testbarkeit testen, damit Sie nur das testen können, was Sie testen möchten. Dies beinhaltet typischerweise die zusätzliche Verwendung von Schnittstellen (ich nehme .NET von dem Code an, den Sie gezeigt haben) und irgendeine Form der Abhängigkeitsinjektion (erfordert jedoch keinen IoC/DI-Container, es sei denn, Sie möchten einen). Es profitiert auch von und ermutigt Sie, sehr zusammenhängende (single purpose) und entkoppelte (soft dependencies) Klassen in Ihrem System zu erstellen.

Wenn Sie also Geschäftslogik testen, die von Daten aus einer Datenbank abhängt, würden Sie normalerweise etwas wie die Repository Pattern verwenden und ein IXXXRepository in Unit-Tests einspeisen. Wenn Sie das konkrete Repository testen, müssen Sie entweder die Art von Datenbankbereinigung durchführen, nach der Sie fragen, oder Sie müssen den zugrunde liegenden Datenbankaufruf shim/stubben. Das liegt wirklich an dir.

Wenn Sie die Datenbank erstellen/ausfüllen/bereinigen müssen, können Sie die verschiedenen in den meisten Test-Frameworks verfügbaren Setup- und Teardown-Methoden nutzen. Aber seien Sie vorsichtig, denn einige von ihnen werden vor und nach jedem Test ausgeführt, was die Leistung Ihrer Komponententests ernsthaft beeinträchtigen kann. Tests, die zu langsam laufen, werden nicht oft ausgeführt, und das ist schlecht.

In MS-Test zeigen die Attribute, die Sie erklären Setup/Teardown sind ClassInitialize, ClassCleanUp, TestInitialize, TestCleanUp verwenden würde. Andere Frameworks haben ähnlich benannte Konstrukte.

Es gibt eine Reihe von Frameworks, die Ihnen mit den spöttischen/Anstoßen helfen: Moq, Rhino Mocks, NMock, TypeMock, Moles and Stubs (VS2010), VS11 Fakes (VS11 Beta), etc. Wenn Sie für Dependency Injection-Frameworks suchen, Schauen Sie sich Dinge wie Ninject, Unity, Castle Windsor usw. an.

0

Ich denke, das Aufräumen hängt davon ab, wie Sie die Daten erstellen, wenn also "alte Testdaten" nicht mit zukünftigen Tests interagieren, denke ich, es ist in Ordnung, es hinter sich zu lassen.

Ein Ansatz, den ich beim Schreiben von Integrationstests gemacht habe, ist, dass die Tests mit einer anderen Datenbank als der Anwendungsdatenbank ausgeführt werden. Ich neige dazu, die Test-Datenbank als Voraussetzung für jeden Testlauf neu aufzubauen. Auf diese Weise benötigen Sie kein granulares Bereinigungsschema für jeden Test, da jeder Testlauf zwischen den Läufen einen sauberen Status erhält. Die meisten meiner Entwicklung habe ich mit SQL-Server gemacht, aber ich habe in einigen Fällen meine Tests gegen eine SQL-Compact-Edition db, die schnell und effizient zwischen den Läufen neu aufzubauen ausgeführt.

0

mbUnit hat ein sehr praktisches Attribut Rollback, das die Datenbank nach Abschluss des Tests bereinigt. Sie müssen jedoch DTC (Distributed Transaction Coordinator) konfigurieren, um es verwenden zu können.

0

Ich hatte ein vergleichbares Problem, bei dem die Behauptung eines Tests die Bereinigung verhinderte und andere Tests fehlschlagen ließ.

Hoffentlich ist das für jemanden irgendwann nützlich.

[Test] 
    public void Collates_Blah_As_Blah() 
    { 
     Assert.False(SINGLETON.Collection.Any()); 

     for (int i = 0; i < 2; i++) 
      Assert.That(PROCESS(ValidRequest) == Status.Success); 

     try 
     { 
      Assert.AreEqual(1, SINGLETON.Collection.Count); 
     } 
     finally 
     { 
      SINGLETON.Collection.Clear(); 
     } 
    } 

Der finally-Block wird ausgeführt, ob die Behauptung besteht oder nicht, es ist auch das Risiko falscher Pässe einführen - was fangen verursachen!