.st0{fill:#FFFFFF;}

Test Smells – Wie man schlecht geschriebene Tests erkennt 

 23. Februar 2023

Der Begriff „Smell“ ist jedem Softwareentwickler ein Begriff. Zumindest sollte er das sein; denn sogenannte Smells bezeichnen einen „schlampigen“ Programmierstil, der mittelfristig negative Auswirkungen haben kann — also etwas, das man bei der Entwicklung von Software auf alle Fälle vermeiden möchte.

Ein typischer Smell im Quellcode ist etwa, wenn zu viel Code dupliziert wird. Dies kann kurzfristig die Arbeit des Entwicklers vereinfachen, mittelfristig hat es aber große Auswirkungen auf die Wartbarkeit von Code und ist eine mögliche Fehlerquelle.

Smells, die sich auf den Quellcode beziehen sind noch geläufig. Neben dupliziertem Code sind das etwa auch zu viele Parameter, unaussagekräftige Variablennamen oder überlange Methoden. Während viele Softwareentwickler Code Smells kennen und es auch gute Werkzeugunterstützung zu ihrer Identifikation gibt, ist den meisten nicht bewusst, dass solche Smells auch in Softwaretests, also Unit-Tests, die den Quellcode prüfen, vorkommen. Wir sprechen dann von sogenannten „Test Smells“.

Test Smells sind Indikatoren für mögliche Missverständnisse und Wartungsproblemen in Testfällen und sollten nach Möglichkeit vermieden werden. Doch wie erkennt man Test Smells? Insbesondere dann, wenn bereits sehr viele Testmethoden geschrieben wurden, dauert es ewig, diese nachträglich zu identifizieren und zu beheben.

In diesem Beitrag beschreiben wir zunächst fünf wichtige Test Smells. Diese treten sehr häufig auf und haben großen Einfluss auf die Wartbarkeit und Überschaubarkeit der Tests. Anschließend stellen wir das Werkzeug „SniffTest“ vor, mit dem diese Test Smells in JUnit Tests erkannt werden können. SniffTest ist im Rahmen einer Abschlussarbeit an der Universität Innsbruck konzipiert, umgesetzt und evaluiert worden. Das Tool und dessen Evaluierung wurde auch publiziert [1]. SniffTest eignet sich sowohl zur nachträglichen Analyse vorhandener Tests als auch zur konstruktiven Integration in eine Entwicklungspipeline.

Wichtige Test Smells und wie man sie findet

Es gibt eine Vielzahl an Test Smells – manche sorgen für Bugs und falscher Sicherheit, andere verringern lediglich die Lesbarkeit des Testcodes. Im Folgenden werden fünf wichtige Test Smells vorgestellt, die besonders häufig auftreten.

Anonymous Test:

Der sogenannte anonyme Test bezeichnet Testmethoden mit einem schlecht-gewählten bzw. nichts-sagenden Namen. Idealerweise reicht der Methodennamen aus, um zu verstehen, welchen Zweck ein Testfall hat und was dieser genau überprüft. In den meisten Fällen wird der Namensgebung jedoch zu wenig Aufmerksamkeit geschenkt, was zu Namen wie „test1“ oder „test2“ führt. Für den Programmierer oder Tester wird es dadurch unmöglich allein am Methodennamen zu erkennen, was der Zweck einer Testmethode ist. Dies verringert nicht nur das Verständnis, sondern erhöht auch den Entwicklungs- und Wartungsaufwand. Was wären also geeignetere Namen für Tests? Im Laufe der Zeit haben sich Best Practices entwickelt. Etwa die folgenden Namensmuster haben sich bewährt:

  • ‚test‘ + Name der getesteten Methode
    • Beispiel: testAdd()
  • Name der getesteten Methode + der getestete Zustand + das erwartete Ergebnis
    • Beispiel: isAdult_AgeLessThan18_False()
  • Eine bestimmte Voraussetzung
    • Beispiel: testIsNotAnAdultIfAgeLessThan18()
  • Eine Aktion + Bedingung / Zustand
    • Beispiel: executeOnDisabledControl()
  • Eingabe / Zustand / Ablauf + Ergebnis
    • Beispiel: Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionWillFail()

Abbildung 1 zeigt Beispiele für eine geeignete und aussagekräftige Namensgebung.

Assertion Roulette:

Eine Testmethode sollte immer nur genau einen Umstand prüfen, d.h. nur ein Assert-Statement beinhalten. Sehr häufig kommt es vor, dass eine Testmethode fünf oder sogar mehr Assertions enthält, wie in Abbildung 1 zu sehen ist. Schlägt nun der Test fehl, muss umständlich nach der Assertion gesucht werden, die fehlgeschlagen ist. Assertion Roulette erhöht daher den Arbeitsaufwand für die Fehleranalyse enorm.

Conditional Test Logic:

Bedingungen und Schleifen sind wichtige Kontrollflusselemente und werden deshalb oft auch in Testcode angewendet. In Testfällen sollte man bedingte Anweisungen jedoch möglichst vermeiden. Statements wie „If-else“, „while“, „for“ oder auch Ausnahmebehandlungen wie „try/catch“ verringern die Lesbarkeit der Tests, erhöhen zudem die Komplexität und erschweren auch die Fehleranalyse, ähnlich wie beim Assertion Roulette. Ein Beispiel hierfür wird in Abbildung 1 gezeigt. Um den Test zu verstehen, muss in diesem Fall zunächst die Verzweigungsstruktur betrachtet werden. Weiters ist der Conditional Test Logic Smell ein möglicher Ausgangspunkt für den nächsten Test Smell.

Rotten Green Test:

Es kann leicht vorkommen, dass ein Test zwar Assertions beinhaltet, diese aber nie ausführt. Befindet sich ein Assert-Statement beispielsweise innerhalb eines If-Blocks und trifft diese Bedingung nicht zu, so wird das Assert-Statement nicht ausgeführt. Gleichzeitig schlägt es auch nicht fehl und der Test gilt als bestanden. Dem Softwareentwickler oder Tester wird damit suggeriert, dass die Software fehlerfrei läuft, da der Test nicht fehlschlägt, doch tatsächlich wurde kein Assert-Statement und damit kein Abgleich von tatsächlichem und erwartetem Verhalten durchgeführt.

Long Test:

Dieser Test Smell beschreibt ein Phänomen, das wohl jedem bekannt ist: zu viele Zeilen Code. Wenn eine Testmethode zu viele Zeilen beinhaltet, verringert das stark die Lesbarkeit und Übersichtlichkeit und erschwert somit auch die Wartbarkeit. Doch was heißt „zu viel“? In einer Studie von Spadini et al. [2] wurde genau diese Frage empirisch untersucht. Es wurden drei Längenbereiche für Testfälle identifiziert: 13 Zeilen (oder genau genommen Statements) sind eine sinnvolle Obergrenze für die Anzahl von Statements in einem Testfall. Von Fall zu Fall können auch noch 19 Statements gut funktionieren. Mehr als 30 Statements gelten jedoch in jedem Fall als zu viel.

JUnit Testcode mit Test Smells
Abbildung 1: JUnit Testcode mit Test Smells

SniffTest – ein Test Smell Detection Tool für JUnit und darüber hinaus

Die Kenntnis von Test Smells alleine ist jedoch nicht genug. Es braucht auch Werkzeugunterstützung, um diese zu identifizieren. SniffTest ist ein von uns entwickeltes Tool, welche die zuvor vorgestellten fünf Test Smells erkennt. Dabei liegt der Fokus auf JUnit-Tests, allerdings werden Algorithmen verwendet, die durch geringe Modifikation auch für andere Frameworks und Programmiersprachen benutzt werden können.

Für die Erkennung wird eine statische Analyse von Java-Dateien durchgeführt. Der Java-Code wird dabei von SniffTest eingelesen und die enthaltenen JUnit-Testmethoden sowie Helfermethoden werden erkannt. Daraufhin werden grundlegende Natural Language Processing Verfahren angewendet, um nach den Symptomen der fünf Test Smells zu suchen. Beim Anonymous Test wird Part-of-speech-Tagging verwendet, um die Testmethodennamen auf die fünf vorgestellten Namensmuster zu überprüfen. Bei den anderen vier Test Smells kommen größtenteils Regular Expressions zum Einsatz, um den Test-Code nach Test Smell Mustern zu durchsuchen.

Die Verwendung von SniffTest erfolgt entweder mittels der Command-Line oder mittels der bereitgestellten graphischen Benutzeroberfläche. Diese vereinfacht unter anderem das Auswählen der zu kontrollierenden Java-Dateien sowie das Anpassen verschiedener Optionen. SniffTest bietet nämlich einige Konfigurationsmöglichkeiten an: so kann man beispielsweise das gewünschte Namensmuster für den Anonymous Test auswählen. Weitere einstellbare Optionen sind:

    – Die maximale Anzahl an Assert-Statements in einer Methode
    – Ob Conditional-Test-Logic für Logging verwendet werden darf
    – Die maximale Anzahl an Statements in einer Testmethode

Nach erfolgreicher Ausführung des Tools kann der Nutzer das Ergebnis als csv-Datei herunterladen. Dies ermöglicht die bessere Analyse des Resultats.

SniffTest [3] wird open-source bereitgestellt, damit interessierte Entwickler das Tool frei einsetzen und nach ihren Vorstellungen und Bedürfnissen anpassen können.

Um die Wirksamkeit von SniffTest zu überprüfen, wurde ein Test-Smell-Datensatz [4] mit 854 JUnit-Testmethoden und 146 Helfermethoden aus insgesamt acht großen GitHub Repositories erstellt. Das Dataset wurde daraufhin manuell auf diese fünf Test Smells kontrolliert und für die Evaluierung des Tools mit den Ergebnissen von SniffTest verglichen. Dabei verzeichnete SniffTest gute Erfolge. Mit einer Genauigkeit von 92,4% wurde der Test Smell Anonymous Test durchschnittlich erkannt. Die Erkennung der Test Smells Assertion Roulette und Rotten Green Tests erfolgte mit einer Genauigkeit von über 99%. Die Test Smells Conditional Test Logic und Long Test würden überhaupt basierend auf den implementierten Mustern vollständig erkannt.

Fazit

Test Smells sind ein wichtiger Faktor für die Verständlichkeit und Wartung von Testfällen sowie die Fehleranalyse. In diesem Beitrag haben wir die fünf wichtigen Test Smells Anonymous Test, Assertion Roulette, Conditional Test Logic, Rotten Green Test und Long Test vorgestellt. Weiters haben wir mit SniffTest ein frei verfügbares Werkzeug vorgestellt, mit dem diese fünf Test Smells automatisch erkannt werden können.

Referenzen

[1] Maier, F., Felderer, M.: “Detection of test smells with basic language analysis methods and its evaluation”, IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER 2023). IEEE, 2023.

[2] Spadini, D., et al.: “Investigating severity thresholds for test smells”, 17th International Conference on Mining Software Repositories (MSR 2020). ACM, 2020.

[3] SniffTest: https://github.com/MaierFlorian/SniffTest

[4] Test-Smell-Datensatz: https://doi.org/10.5281/zenodo.7351414

Zu den Autoren

Florian Maier, Universität Innsbruck

Florian Maier hat an der Universität Innsbruck studiert und 2022 seinen Masterabschluss in Informatik gemacht. Seit 2023 arbeitet er als Softwareentwickler für CompaxDigital in Innsbruck.

Michael Felderer, Universität Innsbruck

Prof. Dr. Michael Felderer ist Direktor des Instituts für Softwaretechnologie am Deutschen Zentrum für Luft- und Raumfahrt e.V. (DLR) sowie Professor an den Universitäten Köln und Innsbruck. Michael Felderer ist international ausgewiesener Experte zu den Themen Softwaretesten und Softwarequalität. Neben seiner wissenschaftlichen Tätigkeit berät er auch Unternehmen und ist regelmäßiger Sprecher auf Konferenzen.