2013-02-20 5 views
9

In Bezug auf Komponententests wurde mir beigebracht, dass der Produktionscode keinen testbezogenen Code enthalten sollte.Sind einzelne Implementierer-Schnittstellen für Unit-Tests eines Antipatterns?

Nun, ich fühle mich wie ich diese Regel jedes Mal brechen, wenn ich versuche, Unit-Test.

Ich habe eine interne Klasse zu meiner Baugruppe, Xyzzy. Ich möchte die Abhängigkeit in eine andere Klasse injizieren und dann stub sie, damit ich die andere Klasse isoliert testen kann, also mache ich eine Schnittstelle, IXyzzy. Hoppla, jetzt habe ich Code in der Produktion, der wirklich nur zum Testen da ist. Noch schlimmer, ich bin irgendwie gegen welche Schnittstelle ist (beschreibt, was ein Implementierer kann tun, nicht was es ist). Xyzys öffentliche Schnittstelle und IXyzzy sind genau gleich und niemand außer den Stubs implementiert IXyzzy.

Das scheint mir eine schlechte Sache.

ich eine abstrakte Basisklasse schaffen könnte oder alle öffentlichen Methoden mache ich auf Xyzzy Overridable/virtual testen wollen, aber das fühlt sich falsch zu, da Xyzzy nicht für die Vererbung und aus einer YAGNI Perspektive konzipiert ist, wird nicht immer von vererbt werden.

Erstellen von Schnittstellen für einzelne Implementierer ausschließlich zum Testen eines Anti-Patterns? Gibt es bessere Alternativen?

Antwort

7

Es ist nicht falsch, Code nur für Tests zu haben. Das ist eigentlich normal, genauso wie Produktionscode Features enthält, die nur zum Debuggen und zur Produktionsüberwachung dienen. Es gibt keinen klaren Grund, warum dies nicht erlaubt sein sollte. Code sollte alle Aspekte des Lebenszyklus der Anwendung unterstützen. Testen ist nur ein weiterer Teil des Lebenszyklus.

In diesem Sinne ist Ihr Ansatz mit Schnittstellen korrekt. Wenn Sie den Rest der Produktionsanwendung dazu verwenden, auch die Schnittstelle zu verwenden (und nicht die konkrete Klasse, obwohl es nur eine gibt), ist dies architektonisch gut.

ich irgendwie bin gegangen, gegen welche Schnittstelle (beschreibt, was ein Implementierer tun, nicht, was es ist)

Ich habe nicht Ihren Punkt hier, da die Schnittstelle, was beschreibt die Objekt kann tun. Es gibt nur eine konkrete (Produktions-) Implementierung, die diese Eigenschaft nicht zerstört.

Wenn man darüber nachdenkt, hat jede Klasse eine "Schnittstelle" im loseren Sinn des Wortes: Die öffentliche Signatur aller Methoden legt eine Schnittstelle offen, die die Klasse nach außen unterstützt. Ob eine .NET-Schnittstelle implementiert ist oder nicht, ist nur ein Detail. Die Klasse macht immer noch die gleichen Versprechen nach außen.

+1

Sehr wahr, viele meiner Schnittstellen, z.B. 'IRequestor', beschreiben Sie, was es tut;) –

3

Meiner Erfahrung nach ist dies ziemlich typisch für die .NET-Entwicklung, da die Methodenüberschreibung auf einer Opt-In-Basis basiert; Wenn Sie eine Abhängigkeit vortäuschen wollen, benötigen Sie entweder eine Schnittstelle oder ein Objekt, dessen Methoden alle virtuell sind.

In einer Sprache wie Java, in der jede Methode übersteuerbar ist, sind Schnittstellen mit einer Implementierung in der Tat ein Antipattern, und gute Entwickler rufen sie auf.

Machen Sie weiter, was Sie tun - welche Sünde Sie auch immer begehen, ist meiner Meinung nach die Vorteile Ihrer Unit-Tests!

+2

Vereinbarte die Vorteile von Komponententests;) Interessanter Artikel darüber, warum C# keine Instanzmethoden hat, die standardmäßig virtuell sind: http://geekswithblogs.net/madhawa/archive /2006/09/17/91418.aspx –

+0

Coole Verbindung, danke! – Ben

2

Ja, es ist ein Anti-Muster.Ein Muster wäre "eine Lösung für ein gemeinsames Problem in einem bestimmten Kontext". Aber in diesem Fall, was wir haben, ist eine Work-Around, keine Lösung. Das fragliche Problem besteht darin, dass eine zu testende Einheit aus (einigen) Abhängigkeiten isolieren muss, so dass die Implementierung dieser Abhängigkeiten beim Schreiben der Komponententests nicht berücksichtigt werden muss. Die allgemeine und wahre Lösung für dieses Problem wird "Mocking" genannt, wobei der Testautor angeben kann, welches Verhalten von den verspotteten Abhängigkeiten benötigt wird.

Im Gegensatz dazu ist das Erzwingen des Entwicklers, unnötige separate Schnittstellen zu erstellen oder Methoden wie virtual zu deklarieren, nur ein Workaround für die technische Unfähigkeit, eine Einheit sauber von anderen zu isolieren.

Für .NET gibt es mehrere Spottwerkzeuge, die diese Isolationsmöglichkeit bieten, nämlich TypeMock Isolator, JustMock und MS Fakes. Andere Sprachen/Plattformen (einschließlich Java, Ruby und Python) haben ihre eigenen Werkzeuge mit ähnlicher Ausdruckskraft.

+1

Es gibt eine gute SO-Frage, die über den letzten Teil Ihrer Antwort spricht: [Verwenden Sie Interfaces Soley, um Stubing und Spott in Unit Tests jetzt veraltet zu erleichtern?] (Http://stackoverflow.com/q/3869002/945456) –