Ein Angebot von

Behaviour Driven Testing und automatische Unit-Test-Generierung

| Autor / Redakteur: Johannes Bergsmann* / Sebastian Gerstl

Bild 1: Grundlegender Ablauf von Test Driven Development
Bild 1: Grundlegender Ablauf von Test Driven Development (Bild: Software Quality Lab)

Softwaretests aus Fachabteilungen sind oft nur funktional, ohne dass Details näher definiert wären. Automatisierte Tests können hier helfen, doch werden sie oft aus Mangel an Fachwissen nicht implementiert. Die agile Methode des BDT (Behavior Driven Testing) zielt darauf ab, die hier entstehende Lücke zu schließen.

In vielen Entwicklungsorganisationen existiert eine Lücke zwischen der Fachabteilung und der Testautomatisierung. Der Fachbereich spezifiziert Tests oft funktional. Es fehlen hier oft Details zum Verhalten. Umgekehrt wird die Implementierung der automatischen Tests durch den Fachtester oft mangels Entwickler-Knowhow nicht verstanden. Sehr oft werden auch zu wenige Tests spezifiziert und automatisiert und die erreichte Testabdeckung ist in manchen Fällen sogar fahrlässig gering.

BDT (Behavior Driven Testing) ist eine Technik aus dem agilen Entwicklungsumfeld, die genau diese Lücke zwischen Fachtestern und Automatisierern schließt. Als Ergänzung zu einem strukturierten Testautomatisierungsansatz (wie z.B. BDT) ist es oft zusätzlich noch sinnvoll, die Testlücken automatisch durch generierte Unit-Tests abzudecken.

Test Driven Development (TDD) als Vorstufe zu BDT

Michael Feathers ist einer der „Gurus“ in der Software-Entwicklung und hat schon vor längerer Zeit den Satz geprägt: „I don't care how good you think your design is. If I can't walk in and write a test for an arbitrary method of yours in five minutes it's not as good as you think it is, …and whether you know it or not, you're paying a price for it!“

Dieses Zitat betont, dass das Verstehen eines Programms (z.B. für einen Tester) einfacher und besser wird, je besser es strukturiert ist. Damit wird natürlich auch die Qualität der Software insgesamt positiv beeinflusst. Um das Verstehen und die Qualität der Software schon möglichst früh sicher zu stellen, ist es daher sinnvoll, bereits in einem sehr frühen Stadium der Software-Erstellung die Tester einzubeziehen. Idealerweise noch bevor die erste Zeile programmiert wird. Dieser Ansatz wird „Testgetriebene Softwareentwicklung“ genannt.

Hier werden durch einen Entwickler oder auch durch einen Tester/Testautomatisierer mit entsprechenden Fähigkeiten die zum Testen der gewünschten Software notwendigen Testfälle bereits VOR der Programmierung spezifiziert und im Idealfall auch automatisiert. Theoretisch ist testgetriebene Softwareentwicklung auch ohne Testautomatisierung umsetzbar. Dieser Ansatz ist jedoch sehr ineffizient. Daher werden bei TDD fast ausschließlich automatisierte Tests verwendet.

Die beschriebenen und automatisierten Testfälle können auch als (detaillierte) Requirements-Spezifikation betrachtet werden. Testfälle sind je im Grunde nur eine andere Form von spezifizierten Anforderungen an die Software.

TDD kann auf mehreren Ebenen angewendet werden:

  • Testen im Kleinen (durch Unit Tests ) und
  • Testen im Großen (durch Integrations- und Systemtests)

Ziele von TDD ist es, von Anfang an testbaren und wartbaren Code zu erstellen und die Testabdeckung zu erhöhen. Ein weiterer Vorteil ist, dass in (agilen) Projekten, in denen oft wenig explizite Requirements-Spezifikation erstellt wird, die Anforderungen an die Software hier aufgrund der Vorgehensweise in Form der Testfälle gut spezifiziert und sogar automatisch überprüfbar definiert werden.

Die Vorteile von TDD sind:

  • Die Qualität der Entwicklung wird verbessert
  • Die Testabdeckung wird erhöht
  • Es existiert eine bessere Basis für die Sprint-Abnahme (Test = Requirements)
  • Die Effizienz im Sprint wird erhöht
  • Es wird eine „lebende“ (Test-)Spezifikation erstellt

Es gibt jedoch auch einige Herausforderungen:

  • TDD alleine macht noch keine guten Requirements und Code
  • Manches kann nicht vorab als Test automatisiert werden (Usablity, System-kontext, Prozessmodellierung, Architektur, etc.)
  • Es ist eine sehr hohe Disziplin erforderlich!

Behaviour Driven Development (BDD) bzw. Behaviour Driven Testing (BDT)

BDD ist eine Variante des TDD. Diese Methode wurde bereits 2003 von Dan North als Antwort auf Test-Driven Development erarbeitet. Grund war, dass Unit-Tests für den Fachbereich, der ja oft kein Entwickler-Knowhow besitzt, schlecht lesbar sind. Die Motivation bei BDD ist, dass die User Stories als weit verbreitete Form der Spezifikation in agilen Projekten die Anforderungen an eine Software sehr oft nur funktional beschreiben. Daher soll durch Behaviour Driven Development – wie der Name schon sagt – eine stärkere Verhaltens- und Prozess-Sicht eingebracht werden.

Bild 2: Struktur der Spezifikation bei Behaviour Driven Development (BDD)
Bild 2: Struktur der Spezifikation bei Behaviour Driven Development (BDD) (Bild: Software Quality Lab)

Die Idee bei BDT ist, dass die User Stories nun auch aus Sicht der Tester durch verhaltensorientierte Testbeschreibungen in einer einfachen aber doch strukturierten Form ergänzt werden sollen, sodass die Tests anschließend einfach automatisiert werden können. Die Beschreibung soll dabei möglichst natürlichsprachlich, leicht verständlich und lesbar erfolgen. Somit ist es möglich, dass auch Nicht-Programmierer einen „Code“ für das automatisierte Testen erstellen.

Der Ablauf bei BDD ist wie folgt:

  • Der Fachbereich oder Product-Owner erstellt User-Stories wie bisher
  • Zusätzlich wird zu einer User Story nun jedoch auch das Verhalten in Form eines “Szenarios” beschrieben
  • Ein Szenario beginnt mit einer Vorbedingung, die aus einem oder mehreren „Steps“ bestehen kann. Ein „Step“ ist ein zusammengehöriges Element, das auch wiederkehrend verwendet werden kann und das einen Teil eines Testfalls (z.B. eine Eingabe oder Ausgabe oder auch eine Aktion) enthalten kann.
  • Wenn die Vorbedingungen gegeben sind, kann im nächsten Schritt die “Aktion” (oder der Trigger) ausgelöst werden.
  • Dies führt dann im dritten Schritt zur „Nachbedingung“ (bzw. Ergebnis) der Aktion.

Tabelle 1: Ableitung für Testfälle.
Tabelle 1: Ableitung für Testfälle. (Bild: Software Quality Lab)

Durch diese Schritte wird ein Teil einer Software sowohl funktional als auch von seinem Verhalten beschrieben. Die Testfälle lassen sich dadurch sehr einfach ableiten. Aus dem oben dargestellten Beispiel können die Tests wie in der hier gezeigten Tabelle abgeleitet werden.

Durch die Parametrierung der Steps können für die Testausfühung beliebige Testdatenkombinationen verwendet werden und somit Standardfälle, Grenzwerte und Fehlerfälle mit einem abstrakten Testfall getestet werden. Damit die in Prosa spezifizierten Tests dann auch einfach automatisiert werden können, gibt es spezielle BDD-Frameworks und BDD-Tools, welche diesen Ansatz unterstützen. In Bild 3 wird dies am Beispiel des Tools „Cucumber“ dargestellt:

Bild 3: Behaviour Driven Development (BDD) am Beispiel Cucumber
Bild 3: Behaviour Driven Development (BDD) am Beispiel Cucumber (Bild: http://www.cukes.info/)

BDD ist ein interessanter Ansatz, der versucht, eine bessere Kopplung zwischen den Beteiligten (Fachexperten, Tester und Entwickler) zu erreichen. Die dabei erstellten automatisierten Regressionstests geben schnelles Feedback, was zu einer „lebenden“ Spezifikation und Dokumentation des Systems führt.

Es gibt jedoch auch hier einige Herausforderungen:

  • Die Fachtester arbeiten typischerweise mit anderen Tools (z.B. Testmanagement-Tools)
  • Es gibt meist keine Tool-unterstützte direkte Kopplung zwischen Testspezifikation und Automatisierungscode, was die Konsistenz der Testfälle mit dem Testcode erschwert. Die in den BDD-Tools verwendeten Code-Aufrufe (Steps) werden oft textbasiert erstellt, ohne diese in der Spezifikationsumgebung mit dem generierten Testautomatisierungscode direkt zu verbinden. Dadurch führt jeder Tippfehler (egal ob beim Tester oder beim Automatisierer) zu einer Lücke im Zusammenhang und bricht damit die Automatisierung. Hier ist noch deutlicher Verbesserungsbedarf bei den Tools.
  • Die derzeit am Markt verfügbaren Tools eignen sich ohne Ergänzung primär für entwicklungsnahe Tests (Unit-/Komponenten-Ebene) und kaum für Systemtests.

Generell kann jedoch BDD/BDT als interessanter Ansatz bewertet werden, der mittlerweile in der Praxis immer mehr Verbreitung findet. Der Bruch zwischen Fachtester und Testautomatisierer kann hier deutlich reduziert werden und mit einer passenden Integration der verfügbaren Frameworks (Testmanagement-Tool mit Automatisierungsumgebung und BDD-Tool) kann diese Lücke auch technisch in den Tools geschlossen werden.

Automatische Unit-Test Generierung

Die zunehmende Komplexität von Systemen führt dazu, dass immer mehr Tests notwendig sind, um dieses Systeme nach dem Stand der Technik abzusichern und die Haftung gegenüber Auftraggebern im Falle von Fehlern zu reduzieren.

Parallel dazu werden für die Entwicklung dieser Systeme immer mehr Software-Entwickler benötigt, die jedoch aktuell am Personalmarkt nicht im benötigten Ausmaß zur Verfügung stehen und dann ev. extern eingekauft werden müssen, was die Entwicklung teurer macht.

Entwickler müssen sich aufgrund der größeren Systemkomplexität auch immer mehr mit der Absicherung des Systems durch passende Unit-Tests beschäftigen (ca. 20-30% der Entwicklerzeit werden für die Unit-Testerstellung aufgewendet). Dadurch wird die Geschwindigkeit der eigentlichen Entwicklung gebremst. Wenn Produkte dadurch erst später auf den Markt gebracht werden können, führt dies zu hohen Umsatzverlusten.

Bild 4: Beispiel einer einfachen Verzweigung. Hier schreibt der Entwickler typischerweise 2 Tests, die einmal den Weg links und einmal rechts gehen. Damit wird 100% Codeabdeckung erreicht; aber um die Verzweigung funktional gut zu testen, sind mindestens 14 Testfälle notwendig.
Bild 4: Beispiel einer einfachen Verzweigung. Hier schreibt der Entwickler typischerweise 2 Tests, die einmal den Weg links und einmal rechts gehen. Damit wird 100% Codeabdeckung erreicht; aber um die Verzweigung funktional gut zu testen, sind mindestens 14 Testfälle notwendig. (Bild: Software Quality Lab)

Die funktionale Abdeckung durch Tests ist oft viel geringer, als es erforderlich wäre. Eine einfache Verzweigung als Beispiel (Grafik links): Hier schreibt der Entwickler typischerweise 2 Tests, die einmal den Weg links und einmal rechts gehen. Damit wird 100% Codeabdeckung erreicht und die meisten sind damit zufrieden.

Um die Verzweigung in der Grafik jedoch funktional gut zu testen, sind mindestens 14 Testfälle notwendig:

  • Jede (Einzel-)Bedingung muss für sich getestet werden (4 Normal-Testfälle)
  • Grenzwerte (oben/unten) der beiden Bedingungen (weitere 4 Testfälle)
  • Fehlertests außerhalb der Grenzen oben und unten sowie innerhalb der Grenzen mit Fehlerwerten (weitere 6 Testfälle)

Die Erstellung von (Unit-)Tests beansprucht daher immer mehr Zeit und Kosten und macht es notwendig, auch die Erstellung der Tests zu automatisieren, soweit dies möglich ist.

Nachfolgend wir ein Ansatz vorgestellt, durch den ein großer Teil der heute oft noch manuell erstellten Unit-Tests automatisch erstellt werden kann.

Technische Umsetzung:

Bild 5: Technische Umsetzung des vorgestellten Testmodells.
Bild 5: Technische Umsetzung des vorgestellten Testmodells. (Bild: Software Quality Lab)

Ablauf:
1. Der Entwickler produziert Code, der aus seiner Sicht passend ist (er findet keine Fehler mehr). Diesen Code übergibt der Programmierer an das Codeverwaltungssystem.
2. Der übergebene Code wird von einem Parser (Programm zur Code-Analyse) ausgelesen, der daraus ein Zwischenmodell der Software erstellt. Das Zwischenmodell enthält alle testrelevanten Elemente (Eingabe, Ausgabe, Schleifen, Verzweigungen, etc.) und deren Zusammenhänge.
3. Dieses Modell verwendet ein Testgenerator als Basis, um daraus sinnvolle Unit-Tests mit einer hohen funktionalen Abdeckung für den Programmcode zu generieren.
4. Die Unit-Tests werden in der zur jeweiligen Programmiersprache passenden Form generiert und können dann in der Programmierumgebung weiterverarbeitet werden, wie wenn diese durch den Entwickler manuell erstellt worden wären.

Die Entwickler können wie bisher einige wenige Unit Tests erstellen, die zum Verständnis der Software hilfreich sind oder eventuell nicht automatisiert werden können. Der Unit Test Codegenerator verbessert dann automatisch die Abdeckung des Codes mit passenden Tests.

Fazit

Test Driven Development (TDD) ist ein guter Ansatz, um die Qualität in der Entwicklung zu verbessern. Es erfordert jedoch sehr hohe Disziplin! Außerdem sind die sehr entwicklernah erstellten automatischen Tests für die Fachtester oft nicht oder schwer lesbar.

Des Weiteren wurde erkannt, dass User Stories meist zu stark funktional spezifiziert werden und die Verhaltens- und Prozess-Sicht fehlt. Daher wurde Behaviour Driven Development (BDD) als Variante von TDD entwickelt. BDD ist für die Zusammenarbeit zwischen Fachtester und TA sehr vielversprechend und hat den Fokus auf dem Verhalten der Software. Die Tester-Sicht wird durch passende Tools nahtlos gekoppelt mit der Testautomatisierung.

Durch die steigende Komplexität in modernen Systemen und auch den Mangel an Software-Entwicklern ist meist eine unzureichende Absicherung durch Unit tests gegeben. Diese Lücke kann aus Kostengründen meist nicht durch manuelle Erstellung von Unit-Tests ausreichend ausgeglichen werden. Es ist daher notwendig, Ansätze für die automatische Absicherung von erstelltem Source-Code anzuwenden. Bestehende Tools bieten hier jedoch meist keine Unterstützung. Bei Software Quality Lab wird gemeinsam mit der technischen Universität Wien ein innovativer Ansatz zur automatischen Test-Code Generierung erforscht und umgesetzt.

Effizient zum Unit-Test unter C++ und C

Effizient zum Unit-Test unter C++ und C

27.06.19 - Continuous Integration und automatisierte Tests sind erprobte Mittel, um die Qualität von in C oder C++ geschriebenem Code zu fördern. Gerade den automatisierten Unit-Tests kommt große Bedeutung zu, garantieren sie doch als Basis der Testpyramide auch die Basis der Qualität. Ein Beispiel aus der Entwicklerpraxis. lesen

Unit Tests mit Python – Beispiele und Fallstricke

Unit Tests mit Python – Beispiele und Fallstricke

04.09.18 - Unit Tests sind wohl die bekannteste Teststufe, die von Entwicklern vor der Integration in die Versionsverwaltung ausgeführt wird. Zunehmend wird dazu die Skriptsprache Python verwendet. Der folgende Beitrag zeigt, wie typische Fallstricke beim Testdesign mit Python umgangen werden können. lesen

(Dieser Beitrag wurde mit freundlicher Genehmigung des Autors dem Tagungsband Embedded Software Engineering Kongress 2018 entnommen.)

Johannnes Bergsmann, Gründer der Software Quality Lab und Geschäftsführer
Johannnes Bergsmann, Gründer der Software Quality Lab und Geschäftsführer (Bild: Software Quality Lab)

Autor

* Johannes Bergsmann ist seit 1988 beruflich im IT-Bereich als Software-Engineer, Consultant, Projektleiter, technischer Leiter und Geschäftsführer tätig. Er ist beeideter Sachverständiger für Informatik und zusätzlich staatlich befugter und beeideter Ziviltechniker für Informatik.

Kommentar zu diesem Artikel abgeben

Schreiben Sie uns hier Ihre Meinung ...
(nicht registrierter User)

Zur Wahrung unserer Interessen speichern wir zusätzlich zu den o.g. Informationen die IP-Adresse. Dies dient ausschließlich dem Zweck, dass Sie als Urheber des Kommentars identifiziert werden können. Rechtliche Grundlage ist die Wahrung berechtigter Interessen gem. Art 6 Abs 1 lit. f) DSGVO.
Kommentar abschicken
copyright

Dieser Beitrag ist urheberrechtlich geschützt. Sie wollen ihn für Ihre Zwecke verwenden? Infos finden Sie unter www.mycontentfactory.de (ID: 46017043 / Test & Qualität)