The Blog

Test Pins

Schreibt man Tests, steht man immer wieder vor dem Problem, dass Felder und Funktionen ausschließlich für Tests öffentlich gemacht werden wollen. In folgendem Diagramm hat der Typ „AktualisiereEinträge“ nur eine öffentliche Funktion „Run()“, an die als Parameter ein Veränderungsmodell übergeben wird. Mehr Funktionalität wird vom Client nicht benötigt:

Bei der Entwicklung kann sich das Bedürfnis ergeben, die Korrektheit der Funktionen SqlAlteDatensaetze() und SqlNeueDatensaetze() zu testen. Im .NET Umfeld ist es ein üblicher Weg Zugriff auf die Methoden zu erhalten,  die Sichtbarkeit der beiden von „private „ auf  „internal“ zu wechseln und in der AssemblyInfo.cs das InternalVisibleTo Attribute zu setzen ([assembly: InternalsVisibleTo(„testAssembly“)]).

Test Pins als Alternative

Eine alternative zur Anpassung der Sichtbarkeit ist das Verwenden von expliziten TestPins. Statt eine Methode ausschließlich für Testcode öffentlich zu machen, wird eine weitere Methode hinzugefügt, die per Namenskonvention anzeigt, dass die Methode ausschließlich für Test genutzt werden soll:

Im Diagramm wurde der Typ „AktualisiereEinträge“ um die Methoden TpSqlNeueDatensaetze() und  TpSqlAlteDatensaetze() erweitert. Dieser Konvention folgend, würden also alle weiteren Testmethoden das Praefix „TP“ erhalten, wobei TP für TestPin steht. So wird klar der Zweck angezeigt. TestsPins könnten auch testvereinfachende Funktionalität besitzen, also Beispielsweise Daten vorbereiten oder schon einfache Assertions durchführen.

Bei der Idee handelt es sich ausschließlich um den Einsatz einer Konvention, Anpassung von Technologie ist nicht notwendig. Denkbar wären auch „Pins“ für tracing, logging und debugging.

Statt von TestPin, der eher an die Elektrotechnik oder Elektronik erinnert, liesse sich auch von TestPlug sprechen.


Kick It auf dotnet-kicks.de

Tags: ,

3 Awesome Comments So Far

Don't be a stranger, join the discussion by leaving your own comment
  1. Rüdiger Plantiko
    Januar 7, 2011 at 7:55 #

    Die TestPins lösen das Problem – es bleibt aber unbefriedigend, dass die Schnittstelle der Klasse – sogar die öffentliche – um Methoden aufgebläht wird, die nur um des Testens willen benötigt werden.

    Alternative 1) Die zu testenden Methoden – wie SqlAlteDatensaetze(), SqlNeueDatensaetze() werden von private auf protected heraufgesetzt, und die Klasse, die die Unit Tests enthält wird von der produktiven Klasse abgeleitet. Dann kann sie zugreifen.

    Alternative 2) – nur in ABAP!: In ABAP gehört die Unittestklasse gewissermassen zu den Details ihres Testees, als sogenannte lokale Klasse (vergleichbar mit einer inneren Klasse in Java). Lokale Klassen können mit dem Konstrukt „local friend“ zum Freund ihres Testees erklärt werden. Die „Aussenwelt“ erfährt von dieser *heimlichen* Freundschaft nichts, aber das Privatleben des Testees ist hiermit vor der Unittestklasse enthüllt.

    Alternative 3) Die Unittestklasse zum Freund des Testees erklären. Dann hat Testee für die testende Klasse keine Geheimnisse mehr.

    Das Testen privater Methoden ist allerdings bedenklich, da es eine Abhängigkeit von den inneren Angelegenheiten des Testees erzeugt, der somit mühsamer änderbar wird.

  2. Robert
    Januar 9, 2011 at 11:05 #

    Hallo Rüdiger,

    zu 1:) die gleichen Bauchschmerzen hatte ich auch. In dem Team in dem ich arbeite, wurde die Verwendung von TestPins auch deswegen etwas verbessert. Die ersten Erfahrungen sind sehr positiv. Die Denke verändert sich und es entsteht eine ganz neue Perspektive auf einzelne Klassen/Komponenten/Funktionseinheiten.

    Hier unsere Änderungen:

    – Test-Pins sind immer „internal“, also werden außerhalb der Assembly nur noch vom Testprojekt gesehen.
    – Methoden bekommen das Attribut [EditorBrowsable(EditorBrowsableState.Never)]
    – Die Namenskonvention ist nochmal deutlicher: TestPins erhalten das Präfix „TP_“.
    – Die TestPins landen in einer Partial class, die wie Code-Behind Dateien in ASP.NET oder Designer Dateien in Winforms mit der ursprünglichen Klasse gruppiert sind, also daher für Assemblies Dritter nicht sichtbar sind.

    Praktisch sieht das so aus:

    partial public class FernglassucheForm //TestPin {

    [EditorBrowsable(EditorBrowsableState.Never)]
    public function TP_TestAufruf(){
    //Tue etwas, dass nur für Tests benötigt wird.
    }
    }

    Zu 2:) Eine Testklasse wie in ABAP, zum Teil des SUTs zu machen, würde für die Art wie wir arbeiten nicht so gut passen. Wenn wir im BDD/ATTD Modus arbeiten, dann betrachten wir immer größere Teile des Systems. Gerade beim Zusammenspiel von vielen Komponenten/Funktionseinheiten, sehe ich die Nützlichkeit von TestPins, da diese auch für Konfiguration u. Setup hilfreich sind. Im TDD Modus haben SUTs oft auch einen Kontext auf dem Sie arbeiten.

    Zu 4:) Innere Abhängigkeiten zu betrachten, finde ich nicht (mehr) bedenklich. Der TestPin oder Diagnose Pin bildet ein Fenster in die Internas, das aber nur für den Zweck von Test und Diagnose besteht. Vor ein paar Wochen hatte ich noch die gleichen Bedenken wie Du, aber durch die TestPin Metapher hat sich meine Sichtweise geändert u. ich finde das ganz natürlich, möglich gut diagnostizieren und testen zu können u. Testfunktionalität als eigenen Aspekt zu betrachten.

    Vielleicht lohnt es sich einfach mal ausprobieren u. dann zu beobachten wie es sich anfühlt.

  3. Rüdiger Plantiko
    Januar 10, 2011 at 8:04 #

    Hallo Robert,

    danke für die Erläuterungen. Du hast einige meiner Bedenken entkräftet!

    Das Problem mit den Abhängigkeiten des Testees von seinen Testklassen bleibt mir allerdings bestehen. Zumindest müsste man dann die Anzahl der TestPins und deren Implementierung – in Deinem Beispiel sind es z.B. reine Delegationen – minimalistisch gestalten. So dass, wenn man z.B. Lust bekommt, sich der privaten Methoden zu entledigen und einen ganz anderen Weg zu beschreiten, nicht erst einmal eine Menge an den Tests und TestPins herumschrauben muss. Wir sind uns ja sicher einig, dass es so einfach wie möglich sein sollte, private Teile von Klassen zu ändern…

    Gruss,
    Rüdiger