Struktur einer wartbaren und skalierenden Fachtest-Suite

Keine Kommentare

Du hast angefangen automatische Fachtests zu schreiben, um nicht in jedem Sprint manuell die Ergebnisse aller vorherigen Sprints testen zu müssen. Prima, wir auch. Nach einer Weile des erfolgreichen Testens ähneln die ganzen Tests aber immer mehr einem Gestrüpp statt einer sauber entworfenen Test-Suite. Mist, bei uns auch. Was ist da schief gelaufen? Im Laufe der Zeit haben sich bei uns einige Muster und Herangehensweisen herausgebildet, die zu einer stabilen, skalierenden und vor allem auch wartbaren Testinfrastruktur führen, welche ich im Folgenden näher vorstellen möchte.

Betrachtet werden soll hier lediglich die Struktur der Fachtests selbst. Außen vor bleiben sämtliche Überlegungen zur konkreten Ausführung (Logging, Parallelisierung) und Hardware. Wie schon des Öfteren erwähnt benutzen wir das Robot Framework zur Fachtest-Automatisierung, deshalb sind einige der vorgestellten Erkenntnisse spezifisch für das Robot Framework. Das meiste lässt sich aber auch auf andere Testframeworks übertragen. Wenn Ihr also Fitnesse, Cucumber, Concordion, etc. verwendet, braucht Ihr nicht gleich aufhören, zu lesen 🙂 Genug des Vorworts, los geht’s:

In letzter Zeit hat es viele, gute Artikel gegeben, die sich mit der Frage beschäftigen, was einen einzelnen, guten Fachtest ausmacht: Da sei zum Beispiel Gojko Adzics Beitrag „Anatomy of a good acceptance test“ empfohlen, oder Dale Emerys Artikel „Writing Maintainable Automated Acceptance Tests“ (PDF), welcher die Ideen mit Codebeispielen und dem Robot Framework illustriert (Uncle Bob zeigt das Gleiche nochmal mit FitNesse). Nun bleibt ein einzelner, exzellent aufgebauter Fachtest aber selten alleine, wie aber schreibt man eine wartbare Test-Suite? Mit Test-Suite bezeichne ich nicht nur die Summe alles Tests, sondern alles, was notwendig ist, um die Tests auch auszuführen (also auch zusätzliche Artefakte, Libraries, Frameworks, etc.). Die Diskussion möchte ich mit einem Zitat aus Dales Artikel einleiten:

The need to change tests comes from two directions: changes in requirements and changes in the system’s implementation. Either kind of change can break any number of automated tests. If the tests become out of sync with either the requirements or the implementation, people stop running the tests or stop trusting the results. To get the tests back in sync, we must change the tests to adapt to the new requirements or the new implementation.

Die Notwendigkeit, Änderungen an der Test-Suite durchzuführen, ergibt sich also zum Einen aus Änderungen an den Anforderungen, und zum anderen aus Änderungen in der Umsetzung. Und was für einen einzelnen Test gilt, gilt natürlich erst recht für eine ganze Test-Suite. Wie soll eine ganze Test-Suite aber aufgebaut sein, um sich den Änderungen möglichst gut anpassen zu können? Offenbar gibt es Teile, die eher stabil sind und Teile, die flexibel und variabel sind.

Stabil sind z.B. das Testframework selbst und die zugehörigen Libraries und Plug-Ins. Diese wird man nicht mal eben austauschen sondern nach Möglichkeit konstant halten. Ebenso konstant sollten die Testfälle selbst bleiben, wenn es keine Geschäftsgrundlage gibt, die Tests zu ändern. Was allerdings problemlos möglich sein sollte: neue Tests unabhängig von den anderen hinzufügen. Wo sind dann die variablen Teile der Test-Suite, wenn sowohl die Tests (gedanklich ganz oben), als auch die Plattform (gedanklich ganz unten) stabil sein sollen. Die nachfolgende Grafik soll dies verdeutlichen und beschreibt den Aufbau einer wartbaren und skalierenden Test-Suite. Die einzelnen Schichten werden unterhalb der Grafik im Detail beschrieben, ganz grob lässt sich die Frage nach den variablen Teilen ganz einfach beantworten: Wenn ich ein System habe, dass an zwei Enden Stabil sein soll, insgesamt aber flexibel, dann müssen die variablen Anteile natürlich in der Mitte sitzen.

Im Folgenden soll der schichtenhafte Aufbau betrachtet werden. Die unterschiedlichen Farben symbolisieren dabei die unterschiedlichen Arten von Dateien im Robot-Framework:

  • rot: Testfälle und Test-Suiten selbst sind durch rote Kästchen hervorgehoben.
  • grün: Robots Resource-Dateien sind grün. Resourcen beinhalten Keywords, die von anderen Resourcen, oder von Testfällen aufgerufen werden können. Vereinfacht gesprochen ist ein Keyword einer Methode nicht unähnlich, eine Resource ist also eine kleine Sammlung von Hilfsmethoden.
  • blau: Elemente des Robot Frameworks selbst sind blau.

Am linken Rand der Grafik ist der Grad der Stabilität, bzw. der Variabilität der Schicht aufgetragen. Oben und unten ergibt sich ein Maximum an Stabilität, welches zur Mitte hin abnimmt und dort ein Maximum der Variabilität aufweist. Das bedeutet, dass die Elemente in der Mitte der Grafik am Häufigsten Änderungen ausgesetzt sind, während die Elemente oben und unten über die Zeit relativ konstant bleiben.

Scalable and Maintainable Acceptance Test-Suite

Scalable and Maintainable Acceptance Test-Suite

Die einzelnen Schichten im Detail:

Test Cases

Die Testfälle selbst sollten ausschließlich angefasst werden müssen, wenn sich die Anforderungen ändern. Unsere präferierte Art und Weise das Verhalten in Robot zu spezifizieren ist das „Given / When / Then“-Format, garniert mit Beispieltabellen (das geht auch auf Deutsch). Andersherum formuliert bedeutet das, wenn sich die Anforderungen nicht ändern, darf es keinen technischen Grund geben, die Dateien mit den Testfällen auch nur anzufassen.

Test Suites

Testfälle sollten zu funktional zusammenhängenden Test-Suites gruppiert werden. Anfangs hatten wir einzelne Testfälle nach der User Story gruppiert, in welcher diese entstanden sind. Im Nachhinein hat sich aber herausgestellt, dass man dann oft suchen musste, wo sich welcher Test versteckte, wenn mehrere thematisch verwandte Stories existierten. Besser ist es, die Test Cases mit der User Story zu taggen, und die Tests nach Thema zu gruppieren.

Imports

Die nächste Schicht habe ich „Imports“ genannt. Ihr Zweck ist das Mapping von Testfällen zu Resource-Dateien, welche die Keywords zur Automatisierung des Fachtests enthalten. Da die Tests selbst nicht durch technische Gründe geändert werden sollen, die Keywords aber laufend refactored und verschoben werden, brauchen wir etwas, das genau dieses Mapping herstellt. Da thematisch verwandte Tests in einer Test-Suite vermutlich ähnliche Imports brauchen, verwenden alle Tests in einer Test-Suite die gleiche Import-Resource. Umgekehrt wird jede Import-Resource nur von Tests aus einer Test-Suite verwendet.

Eigentlich enthalten die Import-Resourcen keine eigenen Keywords. Sie bieten sich allerdings trotzdem für solche „Einmal-Keywords“ an, die ausschließlich in genau dieser Test-Suite verwendet werden, und in keiner der Resource-Dateien aus dem Layer darunter Sinn machen.

Aggregierende Objekte

Die Schicht mit der größten Änderungsfrequenz beinhaltet aggregierende Objekte: Bei Tests einer Weboberfläche hat sich schon das Entwicklungsmuster eines „Page Objects“ etabliert (hier weitere Beispiele für Page Objects mit Selenium selbst oder mit JBehave). Abstrahierend vom Konzept einer Webseite findet in dieser Schicht das klassische Softwaredesign statt: wie wird Code strukturiert, um flexibel um wartbar zu sein. Page Objects sind eine Möglichkeit, wenn es tatsächlich um Webseiten geht. Allgemein können das auch andere Aggregate wie Business- oder Domänenobjekte, Services oder ähnliches sein.

Library Adapter

Wir haben eine Schichte der „Library Adapter“ eingeführt, nachdem wir auf der robotframework-users Mailingliste recht ausführlich über das Thema „Multiple initialization of a Library“ diskutierten (an dieser Stelle ein dickes Dankeschön an Pekka, Janne und die anderen Roboter, die auch auf schräge Fragen schnell und kompetent antworten). Fazit der Diskussion war, dass man Libraries am besten über eine Resource importiert, um sicher zu stellen, dass man nur eine Instanz der Library im System hat. Nach der Einführung der Library Adapter haben sich diese aber auch aus weiteren Perspektiven als nützlich erwiesen: Wird eine Library mit Parametern initialisiert (wie das z.B. bei der SeleniumLibrary der Fall ist) hat man nun nur noch eine Stelle im Code, an der die Parameter stehen (DRY). Desweiteren kann die Library still und heimlich um weitere Funktionalität ergänzt werden,  indem Keywords die thematisch eigentlich in die Library gehörten temporär in dem Library-Adapter aufgefangen werden, bis die entsprechende Funktionalität in der Library abgebildet ist.

Platform

Die Plattform umfasst die installierten Komponenten des Robot Frameworks und der verfügbaren Libraries.

Fazit

Abschließend kann ich feststellen, dass wir mit unserer neuen Struktur der Test-Suite deutliche Fortschritte gemacht haben. Andrerseits lernen auch wir ständig dazu und ich bin mir sicher, dass neue Erkenntnisse weiteren Einfluss auf Struktur und Gliederung der Test-Suite und einzelner Tests haben. Ich hoffe, dass wir unser bisher erworbenes Wissen hier gut zusammenfassen und präsentieren konnten, damit auch andere Projekte davon profitieren können.

Wie sehen Deine Erfahrungen bzgl. der Wartbarkeit von Test-Suites aus? Führen andere Technologien zu anderen Test-Suites? Gibt es in der vorgestellten Zwischenlösung noch Probleme, die uns bisher nicht aufgefallen sind? Ich freue mich auf die Kommentare.

Andreas Ebbert-Karroum

Andreas Ebbert-Karroum ist Agile Principal Consultant bei codecentric und Product Owner von CenterDevice.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.