Wie kann ich testen, ob eine Methode in einer getesteten Klasse aufgerufen wurde?

8

Ich fange damit an zu sagen, dass ich ziemlich neu in Unit-Tests bin und ich würde gerne einen TDD-Ansatz verwenden, aber vorerst schreibe ich Komponententests für einige existierende Klassen, um deren Funktionalität in allen Fällen zu verifizieren.

>

Ich konnte den Großteil meines Codes ohne Probleme mit NUnit und Rhino Mocks testen. Ich habe mich jedoch über Unit-Test-Funktionen gewundert, die am Ende viele andere Methoden innerhalb derselben Klasse aufrufen. Ich kann sowas nicht machen

%Vor%

weil die getestete Klasse keine Fälschung ist. Wenn eine Methode, die ich testet, andere Methoden in der getesteten Klasse aufruft, die wiederum Methoden in derselben Klasse aufrufen, muss ich eine Tonne von Werten fälschen, nur um die "Top-Level" -Methode zu testen. Da ich alle diese "Untermethoden" auch Unit-Tests unterzogen habe, sollte ich davon ausgehen können, dass "SomeMethod" wie erwartet funktioniert, wenn es den Komponententest besteht und Sie sich nicht um die Details dieser untergeordneten Methoden kümmern müssen / p>

Hier ist ein Beispielcode, mit dem ich gearbeitet habe, um meinen Punkt zu verdeutlichen (Ich habe eine Klasse geschrieben, die Import / Export von Excel-Dateien mit NPOI verwaltet):

%Vor%

Sie können sehen, dass ExportExcelDocToDataSet (in diesem Fall meine "oberste" -Methode) GetDataTableFromExcelSheet aufruft, das GetDataRowFromExcelRow aufruft, das a aufruft einige andere Methoden, die innerhalb derselben Klasse definiert sind.

Was ist also die empfohlene Strategie für das Refactoring dieses Codes, um ihn testbarer zu machen, ohne die von Submethoden aufgerufenen Werte stubeln zu müssen? Gibt es eine Möglichkeit, Methodenaufrufe innerhalb der getesteten Klasse zu fälschen?

Vielen Dank im Voraus für jede Hilfe oder Beratung!

    
Gage Trader 11.03.2012, 18:48
quelle

4 Antworten

6

Ändern Sie das Thema im Test (SUT) . Wenn etwas schwierig zu testen ist, kann das Design schwierig sein.

Das Faken von Methodenaufrufen innerhalb der getesteten Klasse führt zu überbestimmten Tests. Das Ergebnis sind sehr brüchige Tests: Sobald Sie die Klasse ändern oder umgestalten, ist es sehr wahrscheinlich, dass Sie auch die Komponententests modifizieren müssen. Dies führt zu zu hohen Wartungskosten für die Stückprüfung.

Um überspezifizierte Tests zu vermeiden, konzentrieren Sie sich auf öffentliche Methoden. Wenn diese Methode andere Methoden innerhalb der Klasse aufruft, testen Sie diese Aufrufe nicht. Auf der anderen Seite: Methodenaufrufe auf anderen abhängig von der Komponente (DOCs) sollten getestet werden.

Wenn Sie dabei bleiben und das Gefühl haben, dass Sie etwas Wichtiges in Ihren Tests vermissen, dann könnte das ein Zeichen für eine Klasse oder eine Methode sein, die zu viel macht. Im Falle einer Klasse: Suchen Sie nach Verstößen gegen das Prinzip der einheitlichen Verantwortung (SRP) . Entwerfe Klassen daraus und teste sie separat. Im Falle einer Methode: Teilen Sie die Methode in mehreren öffentlichen Methoden auf und testen Sie sie einzeln. Wenn das immer noch zu peinlich ist, haben Sie definitiv eine Klasse, die die SRP verletzt.

In Ihrem speziellen Fall können Sie Folgendes tun: Extrahieren Sie die Methoden ExportExcelDocToDataSet und GetDataTableFromExcelSheet in zwei verschiedene Klassen (nennen Sie sie ExcelToDataSetExporter und ExcelSheetToDataTableExporter ). Die ursprüngliche Klasse, die beide Methoden enthält, sollte auf beide Klassen verweisen und die Methoden aufrufen, die Sie zuvor extrahiert haben. Jetzt können Sie alle drei Klassen isoliert testen. Wenden Sie das Extrahieren von Klassen-Refactoring ( Buch ), um die Modifikation Ihrer ursprünglichen Klasse zu erreichen.

Beachten Sie auch, dass Retrofit-Tests immer etwas mühsam zu schreiben und zu warten sind. Der Grund dafür ist, dass die SUTs, die ohne Komponententests geschrieben werden, tendenziell ein umständliches Design haben und daher schwerer zu testen sind. Dies bedeutet, dass die Probleme mit Komponententests durch Modifizieren der SUTs gelöst werden müssen und nicht gelöst werden können, indem die Komponententests aufbereitet werden.

    
Theo Lenndorff 11.03.2012, 19:16
quelle
2

Es spielt keine Rolle, welche Testmethode unter der Haube aufgerufen wird - das sind Implementierungsdetails, und Ihre Unit-Tests sollten sich nicht darüber im Klaren sein. Normalerweise (na ja, die meiste Zeit mit Komponententests) möchten Sie einzelne Einheit testen und sich darauf konzentrieren.

Sie könnten entweder getrennte, isolierte Tests für jede öffentliche Methode in Ihrer Klasse schreiben oder einen Teil der Funktionalität Ihrer getesteten Klasse außerhalb umgestalten. Beide Ansätze konzentrieren sich jedoch auf dieselbe Sache - isolierte Tests für jede Einheit .

Nun, um Ihnen ein paar Tipps zu geben:

  • Wie lautet der Name Ihrer getesteten Klasse? Basierend auf Methoden, die es aussetzt, etwas in der Art von ExcelExporterAndToDataSetConverter ... oder ExcelManager ? Es scheint, als würde diese Klasse zu viele Dinge auf einmal machen ; Dies erfordert ein bisschen Refactoring. Das Exportieren von Daten in DataSet kann leicht von der Konvertierung von Excel-Daten in DataSets / DataRows getrennt werden.
  • Was passiert, wenn sich die Methode GetDataTableFromExcelSheet ändert? Wird in andere Klassen verschoben oder durch Code von Drittanbietern ersetzt? Sollte es Ihre Exporttests brechen? Es sollte nicht - das ist einer der Gründe, warum Ihre Exporttests nicht überprüfen sollten, ob es aufgerufen wurde oder nicht.

Ich schlage vor, zu den DataSet / DataRow-Konvertierungsmethoden zu wechseln, um die Klasse zu trennen - das Schreiben der Komponententests wird erleichtert und die Exporttests sind nicht so anfällig.

    
k.m 11.03.2012 19:10
quelle
1

Ich schätze, Sie testen die öffentliche Methode GetDataTableFromExcelSheet separat, so dass Sie für die Tests von ExportExcelDocToDataSet das Verhalten von GetDataTableFromExcelSheet nicht überprüfen müssen (abgesehen davon, dass ExportExcelDocToDataSet wie erwartet funktioniert).

Eine allgemeine Strategie besteht darin, nur öffentliche Methoden zu testen, da alle privaten Methoden, die Ihre öffentlichen Methoden unterstützen, standardmäßig getestet werden, wenn sich die öffentlichen Methoden wie erwartet verhalten.

Wenn Sie dies weiterführen, können Sie nur das Verhalten einer Klasse testen, anstatt sich auf Methoden als Einheit zu konzentrieren. Dies hilft zu verhindern, dass Ihre Tests brüchig werden - wo die Änderung der Interna der Klasse dazu neigt, einige Ihrer Tests zu unterbrechen.

Natürlich möchten Sie, dass der gesamte Code gut getestet wird, aber ein zu enger Fokus auf Methoden kann zu Versprödung führen; Klassenverhalten testen (tut es, was es auf höchster Ebene tun sollte) testet auch die unteren Ebenen.

Wenn Sie Methoden aus einem Test fälschen möchten, können Sie Ihren Code umgestalten, um eine Schnittstelle für die Methode zu verwenden, die Sie fälschen möchten. Siehe das Befehlsmuster .

In diesem Fall wäre die offensichtliche Änderung für die ExportExcelDocToDataSet eine Arbeitsmappe als Argument zu nehmen. Beim Testen können Sie dann eine gefälschte Arbeitsmappe senden. Siehe Inversion der Kontrolle .

    
Sean 11.03.2012 19:03
quelle
0

Eins ist sicher, Sie machen TDD richtig :) Nun, im obigen Code müssen Sie vor dem Testen der ExportExcelDocToDataSet-Methode die Methode GetDataTableFromExcelSheet vortäuschen.

Aber Sie können stattdessen die von GetDataTableFromExcelSheet zurückgegebene Datentabelle von der Stelle in Ihrem Code übergeben, an der Sie die ExportExcelDocToDataSet-Methode aufgerufen haben, indem Sie einen weiteren Parameter hinzufügen.

so etwas

DataTable dtExcelData = GetData ....; und modifiziere die Methode wie folgt

public DataSet ExportExcelDocToDataSet (bool headerRowProvided, DataTable dtExcelData)

Auf diese Weise müssen Sie GetDataTableFromExcelSheet innerhalb der ExportExcelDocToDataSet-Methode nicht vortäuschen, wenn Sie die ExportExcelDocToDataSet-Methode testen.

    
Dinesh 11.03.2012 19:11
quelle