Angenommen , ich habe einen Klasse - Manager von einer Basisklasse abgeleiteten Mitarbeitern und dass Mitarbeiter haben eine Methode getEmail () , die von geerbt wird Manager . Soll ich testen, ob das Verhalten der getEmail () -Methode eines Managers tatsächlich dem eines Mitarbeiters entspricht?
Zum Zeitpunkt der Erstellung dieser Tests ist das Verhalten dasselbe, aber irgendwann in der Zukunft könnte natürlich jemand diese Methode außer Kraft setzen, ihr Verhalten ändern und daher meine Anwendung unterbrechen. Es scheint jedoch ein bisschen seltsam, im Wesentlichen auf das Fehlen von Einmischcode zu prüfen .
(Beachten Sie, dass das Testen der Manager :: getEmail () -Methode die Codeabdeckung (oder andere Codequalitätsmetriken (?)) Erst verbessert, wenn Manager :: getEmail () erstellt / überschrieben wird.)
(Wenn die Antwort "Ja" lautet, sind einige Informationen zum Verwalten von Tests hilfreich, die zwischen Basisklassen und abgeleiteten Klassen geteilt werden.)
Eine äquivalente Formulierung der Frage:
Wenn eine abgeleitete Klasse eine Methode von einer Basisklasse erbt, wie können Sie ausdrücken (testen), ob Sie von der geerbten Methode Folgendes erwarten:
- Verhalten Sie sich genauso wie die Basis im Moment (wenn sich das Verhalten der Basis ändert, ändert sich das Verhalten der abgeleiteten Methode nicht).
- Verhalten Sie sich für alle Zeiten genauso wie die Basis (wenn sich das Verhalten der Basisklasse ändert, ändert sich auch das Verhalten der abgeleiteten Klasse). oder
- Benimm dich, wie es dir gefällt (das Verhalten dieser Methode ist dir egal, weil du sie nie aufrufst).
quelle
Manager
Klasse abzuleiten,Employee
war der erste große Fehler.Antworten:
Ich würde hier einen pragmatischen Ansatz verfolgen: Wenn in Zukunft jemand Manager :: getMail überschreibt, liegt es in der Verantwortung des Entwicklers, Testcode für die neue Methode bereitzustellen.
Das gilt natürlich nur, wenn
Manager::getEmail
wirklich derselbe Codepfad wieEmployee::getEmail
! Auch wenn die Methode nicht überschrieben wird, verhält sie sich möglicherweise anders:Employee::getEmail
könnte eine geschützte virtuelle aufrufen,getInternalEmail
die in überschrieben wirdManager
.Employee::getEmail
könnte auf einen internen Status (z. B. ein Feld_email
) zugreifen , der sich in den beiden Implementierungen unterscheiden kann: Die Standardimplementierung vonEmployee
könnte beispielsweise sicherstellen, dass dies_email
immer der Fall ist[email protected]
,Manager
ist jedoch flexibler bei der Zuweisung von Mailadressen.In solchen Fällen ist es möglich, dass sich ein Fehler nur in manifestiert
Manager::getEmail
, obwohl die Implementierung der Methode selbst dieselbe ist. In diesem Fall kann eineManager::getEmail
separate Prüfung sinnvoll sein.quelle
Ich würde.
Wenn Sie denken "gut, es ruft wirklich nur Employee :: getEmail () auf, weil ich es nicht überschreibe, also muss ich Manager :: getEmail () nicht testen", dann testen Sie das Verhalten nicht wirklich von Manager :: getEmail ().
Ich würde überlegen, was nur Manager :: getEmail () tun sollte, nicht, ob es vererbt oder überschrieben wird. Wenn das Verhalten von Manager :: getEmail () sein sollte, das zurückzugeben, was Employee :: getMail () zurückgibt, dann ist das der Test. Wenn das Verhalten "[email protected]" zurückgeben soll, ist das der Test. Es spielt keine Rolle, ob es durch Vererbung implementiert oder überschrieben wird.
Was wichtig ist, ist, dass wenn es sich in der Zukunft ändert, Ihr Test es fängt und Sie wissen, dass etwas entweder kaputt ist oder überdacht werden muss.
Einige Leute mögen mit der scheinbaren Redundanz in der Prüfung nicht einverstanden sein, aber mein Gegenargument wäre, dass Sie das Verhalten von Employee :: getMail () und Manager :: getMail () als unterschiedliche Methoden testen, unabhängig davon, ob sie geerbt wurden oder nicht überschrieben. Wenn ein zukünftiger Entwickler das Verhalten von Manager :: getMail () ändern muss, muss er auch die Tests aktualisieren.
Die Meinungen können jedoch variieren, ich denke, Digger und Heinzi haben vernünftige Gründe für das Gegenteil angegeben.
quelle
Testen Sie das Gerät nicht. Führen Sie einen Funktions- / Abnahmetest durch.
Unit-Tests sollten jede Implementierung testen. Wenn Sie keine neue Implementierung bereitstellen, halten Sie sich an das DRY-Prinzip. Wenn Sie sich hier etwas anstrengen möchten, können Sie den ursprünglichen Komponententest verbessern. Nur wenn Sie die Methode überschreiben, sollten Sie einen Komponententest schreiben.
Gleichzeitig sollte durch Funktions- / Akzeptanztests sichergestellt werden, dass Ihr gesamter Code am Ende des Tages das tut, was er soll, und hoffentlich jede Verrückung durch Vererbung auffängt.
quelle
Robert Martins Regeln für TDD sind:
Wenn Sie diese Regeln befolgen, könnten Sie diese Frage nicht stellen. Wenn die
getEmail
Methode getestet werden muss, wäre sie getestet worden.quelle
Ja, Sie sollten geerbte Methoden testen, da sie in Zukunft möglicherweise überschrieben werden. Außerdem können geerbte Methoden überschriebene virtuelle Methoden aufrufen , wodurch sich das Verhalten der nicht überschriebenen geerbten Methode ändert.
Ich teste dies, indem ich eine abstrakte Klasse erstelle, um die (möglicherweise abstrakte) Basisklasse oder Schnittstelle wie folgt zu testen (in C # mit NUnit):
Dann habe ich eine Klasse mit spezifischen Tests für
Manager
und integriere die Tests des Mitarbeiters wie folgt:Der Grund, warum ich das kann, ist Liskovs Substitutionsprinzip: Die Tests von
Employee
sollten immer noch bestehen, wenn einManager
Objekt übergeben wird, da es von stammtEmployee
. Daher muss ich meine Tests nur einmal schreiben und kann überprüfen, ob sie für alle möglichen Implementierungen der Schnittstelle oder Basisklasse funktionieren.quelle
setUp()
Methode von xUnit selbst! Und so ziemlich jedes Web-MVC-Framework, das das Überschreiben einer "Index" -Methode beinhaltet, bricht auch LSP, was im Grunde genommen alles von ihnen ist.class ManagerTests : ExployeeTests
? (dh Spiegeln der Vererbung der getesteten Klassen.) Ist es hauptsächlich ästhetisch, um die Anzeige der Ergebnisse zu erleichtern?Ich würde nein sagen, da es meiner Meinung nach ein wiederholter Test wäre, würde ich einmal in den Mitarbeiter-Tests testen und das wäre es.
Wenn die Methode überschrieben wird, benötigen Sie neue Tests, um das überschriebene Verhalten zu überprüfen. Das ist die Aufgabe der Person, die die übergeordnete
getEmail()
Methode implementiert .quelle
Nein, Sie müssen geerbte Methoden nicht testen. Klassen und ihre Testfälle, die auf dieser Methode basieren, brechen trotzdem ab, wenn sich das Verhalten in geändert hat
Manager
.Stellen Sie sich das folgende Szenario vor: Die E-Mail-Adresse lautet "[email protected]":
Sie haben das Gerät getestet und es funktioniert gut für Ihre
Employee
. Sie haben auch eine Klasse erstelltManager
:Jetzt haben Sie eine Klasse,
ManagerSort
die Manager anhand ihrer E-Mail-Adresse in einer Liste sortiert. Natürlich nehmen Sie an, dass die E-Mail-Generierung mit der folgenden identisch istEmployee
:Sie schreiben einen Test für Ihre
ManagerSort
:Alles funktioniert gut. Jetzt kommt jemand und überschreibt die
getEmail()
Methode:Was passiert jetzt? Ihr
testManagerSort()
wird scheitern, weil dasgetEmail()
vonManager
überschrieben wurde. Sie werden in dieser Ausgabe nachforschen und die Ursache finden. Und das alles, ohne einen separaten Testfall für die geerbte Methode zu schreiben.Daher müssen Sie geerbte Methoden nicht testen.
Ansonsten zB in Java, würden Sie alle Methoden von geerbt testen
Object
wietoString()
,equals()
usw. in jeder Klasse.quelle