Frage

Vor einiger Zeit habe ich das gelesen Mocks sind keine Stubs Artikel von Martin Fowler und ich muss zugeben, dass ich im Hinblick auf die zusätzliche Komplexität ein wenig Angst vor externen Abhängigkeiten habe, deshalb möchte ich fragen:

Was ist die beste Methode für Unit-Tests?

Ist es besser, immer ein Mock-Framework zu verwenden, um die Abhängigkeiten der getesteten Methode automatisch zu simulieren, oder bevorzugen Sie einfachere Mechanismen wie beispielsweise Test-Stubs?

War es hilfreich?

Lösung

Wie das Mantra lautet: „Entscheiden Sie sich für das Einfachste, was funktionieren kann.“

  1. Wenn Fake-Kurse den Job erledigen können, machen Sie mit.
  2. Wenn Sie eine Schnittstelle mit mehreren zu verspottenden Methoden benötigen, verwenden Sie ein Mock-Framework.

Vermeiden Sie die Verwendung von Spott stets weil sie Tests spröde machen.Ihre Tests verfügen nun über detaillierte Kenntnisse über die von der Implementierung aufgerufenen Methoden, wenn sich die simulierte Schnittstelle oder Ihre Implementierung ändert ...Deine Tests scheitern.Das ist schlecht, weil Sie zusätzliche Zeit damit verbringen, Ihre Tests auszuführen, anstatt nur Ihr SUT zum Laufen zu bringen. Tests sollten nicht unangemessen eng mit der Implementierung verbunden sein.
Verwenden Sie also Ihr bestes Urteilsvermögen.Ich bevorzuge Mocks, wenn sie mir das Schreiben und Aktualisieren einer gefälschten Klasse mit n>>3 Methoden ersparen.

Aktualisieren Epilog/Beratung:
(Vielen Dank an Toran Billups zum Beispiel für einen Mockist-Test.Siehe unten)
Hallo Doug, ich denke, wir sind in einen weiteren heiligen Krieg übergegangen – klassische TDDer gegen Mockist-TDDer.Ich glaube, ich gehöre zu Ersterem.

  • Wenn ich mich in Test Nr. 101 Test_ExportProductList befinde und feststelle, dass ich einen neuen Parameter zu IProductService.GetProducts() hinzufügen muss.Ich mache das, um diesen Test grün zu machen.Ich verwende ein Refactoring-Tool, um alle anderen Referenzen zu aktualisieren.Jetzt finde ich, dass alle Spotttests, die dieses Mitglied nennen, jetzt in die Luft fliegen.Dann muss ich noch einmal zurückgehen und all diese Tests aktualisieren – eine Zeitverschwendung.Warum ist ShouldPopulateProductsListOnViewLoadWhenPostBackIsFalse fehlgeschlagen?Liegt es daran, dass der Code kaputt ist?Vielmehr sind die Tests kaputt.Ich bevorzuge das ein Testfehler = 1 zu behebende Stelle.Dem widerspricht die Mocking-Frequenz.Wären Stubs besser?Wenn es so wäre, hätte ich eine fake_class.GetProducts()..Sicher, ein Ort zum Umziehen statt Schrotflintenoperation über mehrere Anrufe hinweg.Am Ende ist es eine Frage des Stils.Wenn Sie eine gemeinsame Dienstprogrammmethode MockHelper.SetupExpectForGetProducts() hätten, würde das auch ausreichen.aber Sie werden sehen, dass das ungewöhnlich ist.
  • Wenn Sie einen weißen Streifen auf dem Testnamen anbringen, ist der Test schwer lesbar.Viele Installationscodes für das Mock-Framework verbergen den tatsächlich durchgeführten Test.
  • erfordert, dass Sie diese besondere Variante eines spöttischen Frameworks erlernen

Andere Tipps

Aufgrund der Erwartungen bevorzuge ich im Allgemeinen die Verwendung von Mocks.Wenn Sie eine Methode für einen Stub aufrufen, der einen Wert zurückgibt, wird normalerweise nur ein Wert zurückgegeben.Aber wenn Sie eine Methode auf einem Mock aufrufen, gibt sie nicht nur einen Wert zurück, sondern erzwingt auch die von Ihnen eingerichtete Erwartung, dass die Methode überhaupt aufgerufen wurde.Mit anderen Worten: Wenn Sie eine Erwartung einrichten und diese Methode dann nicht aufrufen, wird eine Ausnahme ausgelöst.Wenn Sie eine Erwartung festlegen, sagen Sie im Wesentlichen: "Wenn diese Methode nicht angerufen wird, ist etwas schief gelaufen." Und das Gegenteil ist wahr, wenn Sie eine Methode auf einem Mock anrufen und keine Erwartung festgelegt haben, wird sie eine Ausnahme auswerfen und im Wesentlichen sagen: "Hey, was machst du, wenn du diese Methode nennst, wenn du es nicht erwartet hast."

Manchmal möchte man nicht für jede Methode, die man aufruft, Erwartungen haben, daher erlauben einige Mocking-Frameworks „partielle“ Mocks, die Mock-/Stub-Hybriden ähneln, bei denen nur die von Ihnen festgelegten Erwartungen erzwungen werden und jeder andere Methodenaufruf behandelt wird ähnelt eher einem Stub, da er lediglich einen Wert zurückgibt.

Ein sinnvoller Ort für die Verwendung von Stubs, der mir spontan einfällt, ist die Einführung von Tests in Legacy-Code.Manchmal ist es einfach einfacher, einen Stub zu erstellen, indem man die zu testende Klasse in eine Unterklasse umwandelt, als alles umzugestalten, um das Verspotten einfacher oder sogar möglich zu machen.

Und dazu...

Vermeiden Sie immer die Verwendung von Mocks, da diese Tests spröde machen.Ihre Tests verfügen nun über detaillierte Kenntnisse über die von der Implementierung aufgerufenen Methoden, wenn sich die simulierte Schnittstelle ändert ...Deine Tests scheitern.Verwenden Sie also Ihr bestes Urteilsvermögen..<

...Ich sage, wenn sich meine Schnittstelle ändert, sollten meine Tests besser abbrechen.Denn der Sinn von Unit-Tests besteht darin, dass sie meinen Code so genau testen, wie er gerade existiert.

Am besten verwenden Sie eine Kombination, und Sie müssen Ihr eigenes Urteilsvermögen einsetzen.Hier sind die Richtlinien, die ich verwende:

  • Wenn ein Aufruf von externem Code Teil des erwarteten (nach außen gerichteten) Verhaltens Ihres Codes ist, sollte dies getestet werden.Verwenden Sie einen Schein.
  • Wenn es sich bei dem Aufruf wirklich um ein Implementierungsdetail handelt, das die Außenwelt nicht interessiert, bevorzugen Sie Stubs.Jedoch:
  • Wenn Sie befürchten, dass spätere Implementierungen des getesteten Codes versehentlich Ihre Stubs umgehen könnten, und Sie bemerken möchten, wenn das passiert, verwenden Sie einen Mock.Sie koppeln Ihren Test an Ihren Code, aber nur, um zu erkennen, dass Ihr Stub nicht mehr ausreicht und Ihr Test überarbeitet werden muss.

Die zweite Art von Spott ist eine Art notwendiges Übel.Hier geht es wirklich darum, dass Sie unabhängig davon, ob Sie einen Stub oder einen Mock verwenden, in manchen Fällen mehr an Ihren Code koppeln müssen, als Ihnen lieb ist.Wenn das passiert, ist es besser, ein Mock als einen Stub zu verwenden, nur weil Sie wissen, wann diese Kopplung unterbrochen wird und Ihr Code nicht mehr so ​​geschrieben wird, wie es Ihr Test erwartet hat.Es ist wahrscheinlich am besten, einen Kommentar in Ihrem Test zu hinterlassen, wenn Sie dies tun, damit jeder, der ihn kaputt macht, weiß, dass sein Code nicht falsch ist, sondern der Test.

Und wieder ist dies ein Code-Geruch und ein letzter Ausweg.Wenn Sie feststellen, dass Sie dies häufig tun müssen, überdenken Sie die Art und Weise, wie Sie Ihre Tests schreiben.

Es hängt nur davon ab, welche Art von Tests Sie durchführen.Wenn Sie verhaltensbasierte Tests durchführen, möchten Sie möglicherweise einen dynamischen Mock, damit Sie überprüfen können, ob eine Interaktion mit Ihrer Abhängigkeit stattfindet.Wenn Sie jedoch zustandsbasierte Tests durchführen, benötigen Sie möglicherweise einen Stub, um Werte usw. zu überprüfen

Im folgenden Test stellen Sie beispielsweise fest, dass ich die Ansicht ausblende, damit ich überprüfen kann, ob ein Eigenschaftswert festgelegt ist (zustandsbasierter Test).Anschließend erstelle ich ein dynamisches Modell der Serviceklasse, damit ich sicherstellen kann, dass während des Tests eine bestimmte Methode aufgerufen wird (interaktions-/verhaltensbasiertes Testen).

<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
    mMockery = New MockRepository()
    mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
    mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
    mPresenter = New ProductPresenter(mView, mProductService)
    Dim ProductList As New List(Of Product)()
    ProductList.Add(New Product())
    Using mMockery.Record()
        SetupResult.For(mView.PageIsPostBack).Return(False)
        Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
    End Using
    Using mMockery.Playback()
        mPresenter.OnViewLoad()
    End Using
    'Verify that we hit the service dependency during the method when postback is false
    Assert.AreEqual(1, mView.Products.Count)
    mMockery.VerifyAll()
End Sub

Egal, Statist vs.Interaktion.Denken Sie über die Rollen und Beziehungen nach.Wenn ein Objekt mit einem Nachbarn zusammenarbeitet, um seine Aufgabe zu erledigen, dann ist diese Beziehung (wie in einer Schnittstelle ausgedrückt) ein Kandidat für Tests mithilfe von Mocks.Wenn es sich bei einem Objekt um ein einfaches Wertobjekt mit einem gewissen Verhalten handelt, testen Sie es direkt.Ich sehe keinen Sinn darin, Mocks (oder sogar Stubs) von Hand zu schreiben.So haben wir alle angefangen und uns davon umgestaltet.

Für eine längere Diskussion sollten Sie einen Blick darauf werfen http://www.mockobjects.com/book

Lesen Sie die Diskussion von Luke Kanies zu genau dieser Frage in dieser Blogbeitrag.Er verweist ein Beitrag von Jay Fields was sogar darauf hindeutet, dass die Verwendung von [einem Äquivalent zu Rubys/Mochas] stub_everything vorzuziehen ist, um die Tests robuster zu machen.Um die letzten Worte von Fields zu zitieren:„Mocha macht es genauso einfach, einen Mock zu definieren wie einen Stub, aber das bedeutet nicht, dass Sie immer Mocks bevorzugen sollten.Tatsächlich bevorzuge ich im Allgemeinen Stubs und verwende bei Bedarf Mocks.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top