Testautomatisierung

Top 4 Smells in der GUI-Testautomatisierung

Code Smells sind in der Softwareentwicklung bereits seit längerer Zeit ein weit verbreitetes Konzept, um potentielle Probleme in Quellcode effizient zu identifizieren. Bekannt wurde es vor allem durch Martin Fowler und Kent Beck mit dem Buch „Refactoring – Improving the Design of Existing Code„, das die gängigsten Code Smells systematisch beschreibt, kategorisiert und mögliche Bereinigungen in Form von „Refactorings“ beschreibt.
Nachdem nun die Testautomatisierung ein wesentlicher Bestandteil zeitgemäßer Softwareentwicklung ist (siehe Thomas Bucsics Beitrag „Testautomatisierung auf dem Vormarsch!„), erscheint es auch sinnvoll, das Konzept von Code Smells ebenfalls auf diesen Bereich anzuwenden. Das Ziel des Artikels ist daher, die aus meiner Erfahrung, Top 4 (Code) Smells in der GUI-Testautomatisierung zu beschreiben und eventuelle Alternativen aufzuzeigen.

ANECON_Smells in der GUI Testautomatisierung

Als Einleitung findet sich bei Martin Fowler die recht knackige, für den Umfang dieses Artikels allerdings völlig ausreichende, Definition, welche Code Smells wie folgt beschreibt:
A code smell is a surface indication that usually corresponds to a deeper problem in the system.

Dabei sind zwei Punkte besonders hervorzuheben:

  • Code Smells deuteten nicht immer auf ein zugrundeliegendes Problem hin (aber sehr oft)
  • Code Smells müssen auf den ersten Blick ersichtlich sein und keine komplexen Analysetätigkeiten voraussetzten

Für automatisierte GUI-Tests wollen wir uns nun die folgenden vier Smells im Detail ansehen:

  • Die Testfallspezifikation enthält konkrete Testdaten
  • Es existieren Redundante Testartefakte
  • Die Referenz auf UI-Elemente wird nicht zentral verwaltet
  • Es besteht eine direkte Abhängigkeit vom automatisierten Testfall zu einer Ausführungsschicht

 

Die Testfallspezifikation enthält konkrete Testdaten

Der folgende Ausschnitt stellt das Problem beispielhaft anhand eines aufgezeichneten Selenium IDE Testfalls dar:

Wie sofort sichtbar ist enthält die Testfallspezifikation eine Vielzahl von konkreten Testdaten:

  • Benutzername „testUser@anecon.com“
  • Password „test“
  • Produktbezeichnung „Kochbuch“

Für das eigentliche Testziel des Testfalls ist maximal die Produktbezeichnung relevant, die konkreten Benutzerdaten sind jedoch nicht von Bedeutung und sollten nach Möglichkeit entfernt werden (z.B. durch Verwendung eines Data Providers).
Häufig findet man diesen Smell bei aufgezeichneten Testfällen (Capture & Replay) die ohne weitere Bearbeitung verwendet wurden.
Das Problem dabei ist, dass dadurch der Erfolg eines Testfalls von konkreten Testdaten abhängig ist, die inhaltlich nichts mit dem eigentlichen Testziel zu tun haben (das Einloggen von Benutzern ist lediglich eine Vorbedingung). Dadurch besteht die Gefahr, dass bei Fehlern oder Änderungen in den verwendeten Testdaten eine große Anzahl von, inhaltlich nicht zusammenhängenden, Testfällen angepasst werden muss.

Eine optimierte Version könnte hingegen wie folgt aussehen: [Fußnote: Die Implementierung des Data Providers wird hier nicht im Detail vorgestellt, da das den Rahmen des Artikels sprengen würde]

Dieses Beispiel zeigt auch sehr gut, dass ein Code Smell nicht unbedingt auf ein zugrundeliegendes Problem hinweisen muss. Die Verwendung der konkreten Produktbezeichnung kann zum Beispiel akzeptiert werden, wenn sich darauf verlassen werden kann, dass dieses Produkt auf jeden Fall verfügbar sein wird. Außerdem muss klar sein, dass der Testfall durch diese Änderung natürlich noch lange nicht „optimal“ ist, aber ein Code Smell wurde immerhin beseitigt und durch die Einführung einer lokalen Variable „defaultValidUser“ wird auch explizit die Semantik der verwendeten Testdaten dokumentiert und somit das eigentliche Testziel klarer.

 

Es existieren redundante Testartefakte (Quellcode, Testschritte, Testdaten, …)

Wie auch in der Softwareentwicklung stellt redundanter Code ein großes Problem für nachhaltige Softwarekomponenten dar und verursacht unnötige Aufwände und Fehlerpotential. Das Auftreten von redundantem oder dupliziertem Quellcode ist daher ein wichtiger Smell der oft auf eine versteckte, noch nicht implementierte Abstraktion hinweist (beispielsweise eine Methode oder Klasse die diese Funktionalität kapselt).
Wichtig ist, dass sich das nicht nur auf Quellcode beschränkt, denn im Prinzip trifft das auf sämtliche duplizierte Artefakte zu.
Hier daher ein Beispiel, welches die Problematik anhand duplizierter Testschritte verdeutlichen soll:

Eine Reihe von FitNesse Testfällen mit redundanten Testschritten

Reduntante Testschritte in einer Reihe von FitNesse Testfällen einer Suchfunktion

Im Screenshot sind Testfälle FitNesse abgebildet, die unterschiedliche Aspekte der Suchfunktion eines fiktiven Webshops testen. Allerdings beinhalten alle Varianten auch Testschritte („start webshop“, „login with user and password“ und „verify user is logged in“) die für das eigentliche Testziel irrelevant und redundant angeführt wurden. Ändert sich nun ein Aspekt der Authentifizierung oder soll die Suchfunktion auch nicht angemeldeten Benutzern zur Verfügung gestellt werden, dann muss jeder dieser Testfälle manuell angepasst werden.
Weniger änderungsanfällig und wartungsärmer kann man das Beispiel gestalten, wenn man diese Schritte gruppiert und in speziellen SetUp Seiten kapselt. Diese werden fortan automatisch von jedem Testfall referenziert und müssen nur einmal, zentral gewartet werden.

FitNesse Testfall mit Common SetUp Page

Gemeinsame Testschritte in zentrale SetUp Seite extrahiert – mit „Search for valid product“ als Beispieltestfall

 

Referenz auf UI Elemente

Angenommen wir betrachten den folgenden Automatisierten Testfall:

FitNesse Testfall mit Referenz auf UI Elemente

Referenz auf technische Elemente (via CSS Selektoren) in einem FitNesse Testfall

Hier sind in einem Testfall nicht nur die fachlichen Aktionen beschreiben, sondern auch wie sie auf technische Elemente einer Webapplikation abgebildet werden (z.B. durch CSS Selektoren wie „#usernameInput“ oder „#profile .welcomeMessage“). Dadurch ist es notwendig, dass bei technischen Änderungen der Website auch alle davon betroffenen Testfälle angepasst werden müssen, obwohl sich der fachliche Ablauf nicht geändert hat.
Dieses Symptom deutet fast immer darauf hin, dass eine wichtige Abstraktionsschicht noch nicht eingeführt wurde. Wie man damit umgehen möchte hängt wiederum stark von der Situation ab und wie generell bei Smells gilt auch hier, dass nicht jedes Auftreten auch auf ein tatsächliches Problem hindeuten muss.
Im konkreten Beispiel wollen wir aber versuchen die technischen Aspekte so weit wie möglich aus dem Testfall herauszulösen. In FitNesse ist das beispielsweise erreichbar, in dem für jede fachliche Aktion eine spezielle Scenario Table implementiert wird, die eine fachliche Aktion auf das zu testende Systems abbildet (möglich wäre auch der Einsatz von PageObjects, die diese Abstraktion bereits in den Fixtures implementieren). Für das zuvor genannte Beispiel kann das nun wie folgt aussehen:

FitNesse Testfall ohne Referenz auf UI Elemente

Technische Referenzen sind nicht mehr in Testfall enthalten, sondern in Scenario Tables

Wichtig ist das vor allem dann, wenn ein Testfall nicht nur auf einer Technologie durchgeführt werden soll, sondern unterschiedliche relevante Zielplattformen existieren. Beispielsweise ein Testfall der sowohl eine Desktop Webapplikation, mobile Webapplikation und eventuell native mobile Applikationen auf Android und iOS testen soll. In diesem Fall würde das dazu führen, dass der Testfall für jede Technologie dupliziert werden müsste, obwohl der fachliche Ablauf auf jeder Plattform identisch ist.

 

Abhängigkeit zu Ausführungsschicht

Also Ausführungsschicht ist hier jenes Framework oder Werkzeug gemeint, das die Schnittstelle mit dem zu testenden System darstellt – also tatsächlich mit dem Testobjekt interagiert. Bei Webapplikationen kann dafür beispielsweise Selenium, oder auch kommerzielle Werkzeuge wie der Rational Functional Tester von IBM herangezogen werden. Ein Smell der in vielen Projekten im Bereich GUI-Testautomatisierung gefunden werden kann ist, dass zwischen Testfallspezifikation und tatsächlicher Ausführungsschicht eine direkte Abhängigkeit besteht.

Das muss nicht bedeuten, dass das eingesetzte Werkzeug in irgendeiner Form unzureichend ist, allerdings birgt die direkte Abhängigkeit die Gefahr, dass so geschriebene Testfälle weniger einfach wiederverwendet werden können und wartungsanfälliger sind. Im Beispiel kann somit der Testfall nur auf einer konkreten Technologie (SeeTest auf iOS) durchgeführt werden, obwohl die gleiche fachliche Beschreibung auch für andere Technologien gültig sein könnte (z.B. für das Testen einer Webapplikation mit Selenium WebDriver).
Für gut wartbare automatisierte Testfälle ist es daher erstrebenswert möglichst unabhängig von einer konkreten Ausführungsschicht zu sein.
Das kann durch die Implementierung einer zusätzlichen Abstraktionsschicht (im Beispiel „Client“) erreicht werden:

Anders als im Ursprünglichen Beispiel hat das PageObject nun keine direkte Abhängigkeit zu Coded UI und kann daher auch dann wiederverwendet werden, wenn eine andere Automatisierungstechnologie eingesetzt werden soll. Dafür muss lediglich das Interface IWebUIClient implementiert werden und dem PageObject über den Konstruktor übergeben werden.
Aber auch hier gilt: Nicht jedes Auftreten des Smells bedeutet, dass er auf ein tatsächlich zugrundeliegendes Problem hinweist. In vielen Fällen ist ein Werkzeugwechsel nicht vorgesehen oder möglich. In diesen Fällen kann eine direkte Abhängigkeit auf eine konkrete Ausführungsschicht durchaus akzeptabel sein.

 

Fazit

Wie Sie sich sicher vorstellen und aus eigener Erfahrung berichten können, gibt es noch unzählige weitere Beispiele für Smells in der GUI-Testautomatisierung wie beispielsweise: „Fehlende Trennung von Vorbedingungen und eigentlichen Testziel“, „Mehr als ein Testziel pro Testfall“ oder „Fehlende oder unzureichende Fehlerbehandlung und Logging“. Daher die Frage an Sie: welches sind Ihre (un)liebsten Beispiel für Smells in automatisierten Testfällen und vor allem, wie sind Sie damit umgegangen?

 

Passende Artikel