Agile Modellierung mit
UML
Loading

7.4 Sequenzdiagramme

Unter einer Testsequenz wird eine Folge von Eingaben an den Testling verstanden, die dieser während eines Tests bearbeitet. Bei schlecht manipulierbaren Systemen sind einige Anstrengungen zu unternehmen, um zu verstehen, wie Testsequenzen zu entwerfen sind, um bestimmte Situationen herzustellen und das Verhalten des Systems in solchen Situationen zu testen. Demgegenüber hat sich gemeinsam mit der Objektorientierung verstärkt eine Vorgehensweise durchgesetzt, nur die Testdaten direkt vor der Testausführung zu erstellen, statt einen Pfad von einem Startzustand bis zu diesem Datensatz anzugeben. Dies hat mehrere Gründe. Zum einen sind aufgrund geänderter Codierungsstandards im objektorientierten Ansatz Methoden typischerweise sehr viel kleiner als die Prozeduren der imperativen Welt. Zum zweiten kann mit dynamischer Bindung und Bildung von Unterklassen der Testling besser kontrolliert und manipuliert werden, indem ihm an kritischen Stellen Dummies statt der echten Implementierung angeboten werden. Zum dritten hat sich die Erkenntnis durchgesetzt, dass der Code durchaus so gestaltet oder umgestaltet werden sollte, dass er gut testbar ist. Zum Beispiel wird in dem in Kapitel 8 vorgestellten Ansatz die Erzeugung neuer Objekte in eine Factory ausgelagert, um in einem Factory-Dummy auch diese Erzeugung kontrollieren zu können. Durch diese Maßnahmen ist keine Sequenz von Aufrufen mehr notwendig, um eine bestimmte Situation im System herzustellen. Diese kann direkt als Testdatensatz konstruiert werden. Deshalb kann ein Großteil der Testfälle durch einen einfachen Methodenaufruf gesteuert werden, nachdem der adäquate Testdatensatz erstellt wurde. Diese Vorgehensweise birgt allerdings das Risiko, dass der benutzte Testdatensatz im realen System gar nicht auftreten kann.

Durch die Zerlegung der Funktionalität in viele objektorientierte Methoden entstehen jedoch komplexere Aufrufhierarchien. Die Objekte bilden damit komplexere Interaktionsmuster, die durch die Vor- und Nachbedingung einer Methode oft nicht adäquat erfasst werden. Für das Verständnis des Zusammenspiels zwischen verschiedenen Objekten oder Systemteilen ist es daher sinnvoll, diese Interaktionsmuster in Form von Sequenzdiagrammen zu modellieren. Ein Sequenzdiagramm ist eine exemplarische Darstellung eines möglichen Ablaufs. Deshalb eignet sich ein Sequenzdiagramm in idealer Weise, den internen Ablauf eines Tests zu beschreiben. Beispielsweise beschäftigt sich [PJH+01] mit der Verwendung eines an die UML angelehnten MSC-Dialekts für die Konformitätstests von Telekommunikationsprotokollen.

Im Folgenden wird anhand einiger Beispiele demonstriert wie Sequenzdiagramme zur Modellierung von Testfällen verwendet werden können.

7.4.1 Trigger


Abbildung 7.8: Sequenzdiagramm als Testfallbeschreibung

Das in Abbildung 7.8 gezeigte Beispiel nutzt den in Abschnitt 6.1, Band 1 definierten Stereotyp trigger, um den ersten Methodenaufruf als Auslöser zu kennzeichnen. Damit müssen notwendigerweise die weiteren angegebenen Methodenaufrufe und Returns stattfinden, damit der beschriebene Test erfolgreich ist. Zusätzliche OCL-Bedingungen können innerhalb des Sequenzdiagramms angegeben werden, um Zwischenzustände zu prüfen sowie eine finale Prüfung des Ergebnisses vorzunehmen. Das Sequenzdiagramm zeigt nicht alle stattfindenden Methodenaufrufe. Es abstrahiert zum Beispiel von Interaktionen zwischen dem Auktionsobjekt und den anderen teilnehmenden Personen, die ebenfalls eine Mitteilung erhalten. Dieser Aspekt wird also mit dem angegebenen Sequenzdiagramm nicht getestet.

Um den Test zu vervollständigen, ist für den Ablauf neben dem Sequenzdiagramm ein Testdatensatz notwendig, der zum Beispiel durch ein Objektdiagramm beschrieben werden kann. Durch die Verwendung eines Objektdiagramms werden die im Sequenzdiagramm angegebenen Objekte in eine strukturelle Beziehung durch Links gesetzt, die im Sequenzdiagramm nicht dargestellt werden kann. Aufgrund der Komplexität der konstruktiv und damit vollständig zu modellierenden Struktur, empfiehlt es sich im Beispiel, diese Struktur durch zwei Objektdiagramme zu beschreiben. Im Beispiel wird angenommen, dass eine vervollständigte und damit für konstruktive Testdatengenerierung verwendbare Fassung des Objektdiagramms aus Abbildung 5.13 unter dem Namen Kupfer912 zur Verfügung steht. Abbildung 7.9 beinhaltet damit die vollständige Beschreibung des Tests auf Basis des Sequenzdiagramms HandleBid.


Abbildung 7.9: Test nutzt Sequenzdiagramm HandleBid

Da es ein Sequenzdiagramm erlaubt, OCL-Bedingungen zu verschiedenen Punkten im Ablauf des Systems anzugeben, ist die Verwendung einer zusätzlichen Methodenspezifikation nicht notwendig, obwohl eine solche im Test zusätzlich angegeben werden kann.

Für die Prüfung der angegebenen Interaktionen sowie der OCL-Bedingungen während des Ablaufs eines Sequenzdiagramms ist es notwendig, den Code geeignet zu instrumentieren. So muss, wie in Abschnitt 5.5 beschrieben, jeder Methodenaufruf und jede return-Anweisung protokolliert werden. Zusätzlich sind die OCL-Bedingungen zu prüfen. Die Prüfung, der Aufbau der Testdaten und die Ausführung des Tests werden im Testtreiber lokalisiert, der als von JUnit aufrufbare Methode in der Klasse AuctionTest abgelegt wird.

7.4.2 Vollständigkeit und Matching

Gemäß Abschnitt 6.3, Band 1 gibt es mehrere Interpretationsmöglichkeiten für ein Sequenzdiagramm. So kann durch Awendung des Stereotyps match:complete festgelegt werden, dass alle Interaktionen des Objekts im dargestellten Zeitraum im Sequenzdiagramm gezeigt werden. Diese für Spezifikationen meist unpraktikabel starke Aussage hat im Test seine Berechtigung, da die getesteten Objekte ausschließlich zum Zweck des Tests erzeugt wurden und weder davor, noch danach benutzt werden.

Der Stereotyp match:visible wirkt etwas schwächer, indem er nur fordert, dass alle Interaktionen zwischen den im Diagramm angegebenen Objekten gezeigt werden, aber weitere Methodenaufrufe an andere Objekte möglich sind. Damit ist match:visible ebenfalls eine nützliche Interpretation von Sequenzdiagrammen für Tests.

Die Verwendung von match:initial erlaubt den getesteten Klassen weitere Freiheiten, da das Sequenzdiagramm nach Auslösung des Triggers jeweils nur die angegebenen Interaktionen fordert, aber weitere zulässt. Damit gehen aber einige Kontrollmöglichkeiten verloren, die gerade für Tests von Interesse sind. Die Interpretation mit dem Stereotyp match:free ist daher für Tests kaum mehr geeignet, wenn der Stereotyp auf das komplette Sequenzdiagramm angewandt wird. Eine interessante Technik bietet allerdings die kombinierte Verwendung von Stereotypen, wie in Abbildung 7.10 gezeigt.


Abbildung 7.10: Kombinierte Verwendung von Stereotypen

Bei Anmeldung einer neuen Person in einer Auktion werden dem Personenobjekt durch die Methode sendMessage nacheinander alle aufgetretenen Nachrichten zugestellt. Dabei wird sendMessage mehrfach aufgerufen. Das Sequenzdiagramm fordert nun, dass ein Aufruf dabei ist, der die angegebenen Eigenschaften erfüllt, bevor die übergeordnete login-Methode abgearbeitet ist.

7.4.3 Nicht-kausale Sequenzdiagramme

Wie in Abschnitt 6.4, Band 1 beschrieben, kann ein Sequenzdiagramm unvollständig und damit nicht-kausal sein. Beispielsweise kann gewollt sein, dass ein an der Ausführung beteiligtes Objekt oder einzelne Methodenaufrufe nicht beobachtet werden sollen, diese aber weitere beobachtete Methodenaufrufe zur Folge haben. Derartige Sequenzdiagramme sind für Tests ebenfalls geeignet, wie das Beispiel in Abbildung 7.11 zeigt.


Abbildung 7.11: Nicht-kausales Sequenzdiagramm als Testablaufbeschreibung

Die zeitliche Folge der Methodenaufrufe wird alleine durch deren Reihenfolge entlang der Zeitachse festgelegt und kann somit überprüft werden. Die Kausalität kann allerdings nicht beliebig aufgehoben werden. Natürlich können return-Pfeile nur angegeben werden, wenn der zugehörige Methodenaufruf angegeben ist.

7.4.4 Mehrere Sequenzdiagramme in einem Test

Da ein Sequenzdiagramm eine Beobachtung beschreibt, ist es möglich, mehrere Sequenzdiagramme für verschiedene Teilabläufe innerhalb eines Tests einzusetzen. So ergänzen sich die beiden Diagramme aus den Abbildungen 7.8 und 7.11. Dabei werden die Sequenzdiagramme unabhängig voneinander geprüft, also jeweils so, als wenn kein anderes Sequenzdiagramm vorhanden wäre. Die in beiden Sequenzdiagrammen gemeinsam auftretenden Methodenaufrufe, im Beispiel also handleBid, werden daher im Testablauf durch denselben Methodenaufruf erfüllt. Eine sequentielle oder alternative Komposition oder Iteration von Sequenzdiagrammen, wie sie MSC’s [IT11], der UML-Standard [OMG10] oder auch YAMS [Krü00] erlauben, wäre eine mögliche Erweiterung, ist jedoch, wie in Kapitel 6, Band 1 begründet, in UML/P nicht vorgesehen. Stattdessen unterliegt der Interpretation mehrerer Sequenzdiagramme eine lose Form der „Verschmelzung“, die gleiche Methodenaufrufe identifizieren kann und so die Sequenzdiagramme unabhängig voneinander einsetzt.

7.4.5 Mehrere Trigger im Sequenzdiagramm

Testklassen werden entsprechend mit den in JUnit üblichen Codierungsstandards mit dem Suffix „Test“ versehen oder, wie in Abschnitt 2.5.2, Band 1 vorgeschlagen, durch einen Stereotyp wie Testclass markiert. Weil die Methodenaufrufe zum Start des Tests ausschließlich vom Testtreiber vorgenommen werden können, umgekehrt aber auch jeder Methodenaufruf, ausgehend von der Testklasse als Trigger zu verstehen ist, ist bereits einer der Stereotypen trigger oder Testclass bei der Definition von Testfällen ausreichend.

Wie Abbildung 7.12 zeigt, kann ein Sequenzdiagramm auch mehrere mit dem Stereotyp trigger markierte Methodenaufrufe beinhalten. Ein solches Sequenzdiagramm beschreibt einen Testtreiber, der nacheinander mehrere Methodenaufrufe durchführt.


Abbildung 7.12: Sequenzdiagramm mit aufeinanderfolgenden Triggern

7.4.6 Interaktionsmuster

Die Verwendung eines Sequenzdiagramms zur Definition eines Testfalls hat den wesentlichen Vorteil, dass durch das Sequenzdiagramm das geprüfte Interaktionsmuster explizit dargestellt ist. Durch Abgleich mit einer vorhandenen Implementierung lässt sich relativ leicht analysieren, ob und wie gut eine gegebene Menge von solchen Testfällen die verschiedenen möglichen Abläufe im Testling abdeckt.

Eine vollständige Überdeckung aller möglichen Abläufe eines Testlings ist aber meist nicht möglich, denn ganz ähnlich dem Pfadüberdeckungstest einer Prozedur [Bal98Mye01] führen Schleifen und Rekursion zu einer unbeschränkten Anzahl von Varianten möglicher Abläufe. Für praktische Belange ist es deshalb notwendig, eine endliche, möglichst repräsentative Menge von Abläufen festzulegen, die in Testfällen umgesetzt wird. Im Auktionsprojekt sind dies zum Beispiel Auktionen mit verschiedenen Mengen an Geboten (0,1,2,3,5 und sehr viele), die in Testfällen geprüft worden sind. Solche Tests, die den Ablauf einer Teilphase oder sogar der gesamten Auktion prüfen, gehen über den einzelnen Methodentest hinaus und bilden damit einen ersten Schritt zur Integration verschiedener Systemteile. Sequenzdiagramme sind daher auch geeignet, Integrationstests zu modellieren. Dabei ist es sinnvoll, bei Integrationstests vor allem die Schnittstellen zwischen den Systemteilen zu modellieren und die Kapselung der Systemteile selbst zu respektieren. Dafür sind Sequenzdiagramme mit Stereotypen wie match:free geeignet.

Weitere Probleme, eine Überdeckung der Interaktionsmuster zu erzielen, ergeben sich aus der explodierenden Anzahl von möglichen Objektstrukturen, die als Grundlage für die Abläufe dienen:

  • Mengenwertige Assoziationen erlauben grundsätzlich eine unendliche Anzahl verschiedener Objektstrukturen, die bei einer Bearbeitung ebenfalls Schleifen benötigen und daher wieder zu unendlich vielen möglichen Ablaufstrukturen führen.
  • Die sich aus der Vererbung ergebende dynamische Bindung von Methoden erfordert es, für ein gegebenes Objekt einer Klasse auch alle Unterklassen zu testen. Dies führt bei mehreren Objekten im Testdatensatz zu einer Explosion der Testfallanzahl.
  • Die dynamische Veränderbarkeit von Objektstrukturen macht das Ablaufverhalten eines Testlings auch von den Parametern abhängig. Beispielsweise kann ein Parameter bestimmen, wieviele Objekte in einer Datenstruktur erzeugt oder wie diese durch Links verbunden werden.

Deshalb ist es für praktische Belange unrealistisch, eine vollständige Überdeckung möglicher Abläufe durch Testfälle auf Basis von Sequenzdiagrammen zu erwarten. Wie das folgende Beispiel zeigt, ist damit außerdem auch nicht gesichert, dass eine Anweisungsüberdeckung der getesteten Methoden stattfindet:

void methode(int i) { Java   
  if(i >= 1)
    foo(i);
  else {
    attribut = i;
    foo(i+1);
  }
}

Alle Sequenzdiagramme zum Test von method zeigen dieselbe Ablaufstruktur. Es ist daher falsch, anzunehmen, dass zwei Sequenzdiagramme mit derselben Ablaufstruktur dieselben Abläufe der Implementierung testen.

Deshalb ist die Definition von Integrationstests mit Sequenzdiagrammen nur eines von mehreren Instrumenten zur Testfallmodellierung, das gemeinsam mit den bereits besprochenen Testfällen aus Methodenspezifikationen und der Prüfung von Invarianten einzusetzen ist.


Bernhard Rumpe. Agile Modellierung mit UML. Springer 2012