Dojos für Entwickler. Stefan Lieser

Чтение книги онлайн.

Читать онлайн книгу Dojos für Entwickler - Stefan Lieser страница 8

Dojos für Entwickler - Stefan Lieser

Скачать книгу

Bei der testgetriebenen Entwicklung steht der Test vor der Implementierung, also gilt es, Testdaten zu erstellen. Ich habe mich zunächst um das „Happy Day Szenario“ gekümmert, also einen Testfall, der später bei der Verwendung typisch ist, siehe Listing 4.

      Listing 4: Eine einfache Testklasse.

       Ledigpublic

       class ClassWithPublicGettersAndSetters

       {

       public string StringProperty { get; set;}

       public int IntProperty { get; set;}

       }

      Als Nächstes folgt eine Klasse, deren Properties private sind. Diese sollen in den Tests unberücksichtigt bleiben, ihr Name darf also nicht geliefert werden. Die Implementierung der Funktion ist mit LINQ ganz einfach, siehe Listing 5.

      Listing 5: Die zu prüfenden Properties finden.

       internal static IEnumerable<string> FindPropertyNames(Type type) {

       return type.GetProperties(PropertyBindingFlags)

       .Where(propertyInfo => propertyInfo.CanRead)

       .Where(propertyInfo => propertyInfo.CanWrite)

       .Select(propertyInfo => propertyInfo.Name);

       }

      Die beiden Where-Klauseln sorgen dafür, dass nur Properties berücksichtigt werden, die sowohl einen Getter als auch einen Setter haben. Durch die Binding Flags werden schon Properties ausgeschlossen, die nicht public sind. Durch die Select-Klausel wird festgelegt, wie die zu liefernden Ergebnisse aufgebaut sein sollen.

      FindPropertyTypes

      Die Funktion FindPropertyTypes erhält als Argumente die Liste der Property-Namen, die berücksichtigt werden sollen, sowie den Typ, zu dem die Properties gehören. Dazu liefert sie jeweils den Typ der Properties. Auch diese Tests benötigen wieder Testklassen. Ich habe einfach die schon vorhandenen Testklassen verwendet. Auch hier ist die Implementierung dank LINQ nicht schwierig.

      GenerateValues

      Um die Property-Setter später aufrufen zu können, muss jeweils ein Objekt vom Typ der Property erzeugt werden. Diese Aufgabe übernimmt die Funktion GenerateValues. Sie erhält als Argument die Liste der Typen und liefert dazu jeweils eine Instanz. Die Funktion ist derzeit recht einfach gehalten. Die Instanz wird einfach durch Verwendung von Activator.CreateInstance erzeugt. Lediglich Strings werden gesondert behandelt, da die Klasse über keinen parameterlosen Konstruktor verfügt, siehe Listing 6.

      Listing 6: Passende Objekte erzeugen.

       internal static IEnumerable<object> GenerateValues(this IEnumerable<Type> types) {

       return types.Select(type => CreateInstance(type));

       }

       internal static object CreateInstance(Type type) {

       if (type == typeof(string)) {

       return "";

       }

       return Activator.CreateInstance(type);

       }

      Die Methode CreateInstance muss sicher im Laufe der Zeit angepasst werden. Sie ist in der gezeigten Implementierung nicht in der Lage, mit komplexen Typen zurechtzukommen.

      GenerateTestMethods

      Nun stehen alle Informationen zur Verfügung, um für jede Property eine Testmethode zu erzeugen. Die Funktion Generate-TestMethods erhält drei Argumente:

       die Liste der Werte für die Zuweisung, I die Liste der Property-Namen,

       den Typ, auf den sich die Tests beziehen.

      Das Ergebnis ist eine Liste von Actions.

      static IEnumerable<Action<object>>

       GenerateTestMethods(this IEnumerable<object>

       values, IEnumerable<string> propertyNames,

       Type type)

      Das Testen dieser Funktion kommt leider auch wieder nicht ohne Testklassen aus, denn der Typ geht ja als Argument in die Funktion ein. Die erzeugten Testmethoden werden im Test aufgerufen, um so zu prüfen, dass sie jeweils einen bestimmten Aspekt der INotifyPropertyChanged-Semantik überprüfen. Hier wird es schon schwierig, die Vorgehensweise zu beschreiben, da es sich um Tests handelt, die testen, dass generierte Testmethoden richtig testen, sozusagen Metatests.

      Die Implementierung der Funktion hat es ebenfalls in sich. Zunächst müssen zwei Aufzählungen „im Gleichschritt“ durchlaufen werden. Dazu wird der Enumerator einer der beiden Aufzählungen ermittelt. Anschließend wird der andere Enumerator in einer foreach-Schleife durchlaufen. Innerhalb der Schleife wird der erste Enumerator dann „per Hand“ mit MoveNext und Current bedient. Ich hätte dies gerne in eine Methode ausgelagert, das ist jedoch durch die Verwendung von yield return nicht möglich.

      Damit sind wir bei der zweiten Besonderheit der Funktion. Die einzelnen Testmethoden werden jeweils mit yield return zurückgeliefert. Da das Ergebnis der Funktion eine Aufzählung von Actions ist, liefert das yield return jeweils eine Action in Form einer Lambda Expression. Dabei müssen die Werte, die aus den Enumeratoren in der Schleife entnommen werden, in lokalen Variablen abgelegt werden, damit sie als Closure in die Lambda Expression eingehen können. Andernfalls würden am Ende alle Lambda Expressions auf demselben Wert arbeiten, nämlich dem aus dem letzten Schleifendurchlauf. Auch hier macht sich übrigens wieder mal der Einsatz von JetBrains ReSharper bezahlt. Der weist nämlich mit der Warnung „Access to modified closure“ auf das Problem hin.

      ExecuteTestMethods

      Der letzte Schritt im Flow ist die Ausführung der erzeugten Testmethoden. Diese Methode ist erst durch eine Refaktorisie-rung entstanden, daher teste ich sie nicht isoliert, sondern nur im Integrationstest.

      Und jetzt alle!

      Nun müssen nur noch alle Flowstages zusammengesteckt werden. Das ist einfach, da die Stages als Extension Methods implementiert sind. Dadurch können sie hintereinandergereiht werden, wie Listing 7 zeigt.

      Listing 7: Flowstages zusammenstecken.

       public static void Verify(Type type) {

       var propertyNames = type

       .FindPropertyNames();

       propertyNames

       .FindPropertyTypes(type)

       .GenerateValues()

       .GenerateTestMethods(

       propertyNames, type)

       .ExecuteTestMethods(type);

Скачать книгу