In diesem Post und dem nächsten möchte ich beschreiben, wie Akzeptanztests in einer DSL formuliert werden könnten und wie der Ablauf zum Beschreiben dieser Tests aussieht. Außerdem werde ich auf mögliche Probleme eingehen.
Dazu werde ich einen kleinen Use Case als Beispiel heranziehen und auf Basis dessen mehrere Testfälle beschreiben. Erst einmal sind die Eigenschaften der DSL und ein paar allgemeine Informationen an der Reihe. Den kompletten Modellierungs- bzw. Testbeschreibungsablauf werde ich im nächsten Post durchgehen.
Grundlegende Aktionen in der DSL
Zunächst einmal geht es in dem System, welches als Ziel dieses Blogs entwickelt werden soll, um das Testen von datenbankbasierten Applikationen. Deshalb liegt zunächst die Idee nah, Datenbankabfragen bzw. – manipulationen durchzuführen, welche den Anfangs- und Endzustand eines Akzeptanztests beschreiben.
Grundsätzlich erzeugt man anfangs bestimmte Daten in der Datenbank bzw. überprüft einen bestimmten Anfangszustand, dann führt man die zu testende Funktion durch und letztendlich überprüft man gezielt bestimmte Tabellen in der Datenbank, ob die getestete Funktion auch ihr Ziel erfüllt hat.
Um dies durchzuführen, benötigt man bestimmte Aktionen, welche das System implementiert. Ähnlich wie in SQL gibt es eine Art “Insert-Befehl” zum Erzeugen und anschließendem Einfügen von Daten und einen “Select”-Befehl zum Selektieren der Daten. Außerdem sollte es eine Aktion zum Überprüfen von Daten geben, ähnlich einer Assertion, wie man sie aus verschiedenen Programmiersprachen kennt, damit man beispielsweise Objekte auf Gleichheit oder Ähnliches prüfen kann. Schlägt so eine Assertion fehl, so ist auch der Testfall negativ ausgefallen.
Im folgenden sind ein paar Beispiele aufgeführt, welche ich direkt aus dem momentan von mir entwickelten Prototyp entnommen habe. Die Entwicklung der DSL an sich ist noch nicht abgeschlossen, deshalb kann sich die Notation noch ändern.
Beispiel für Erzeugung
Erzeuge Zimmer zimmer1 {
Zimmernummer = "1"
Zimmerart = Einzelzimmer
}
Erzeuge Zimmer zimmer2 {
Zimmernummer = "2"
Zimmerart = Doppelzimmer
}
Erzeuge Beherbergungsbetrieb betrieb1 {
Betriebsart = Hotel
Bezeichnung = "Hotel am Park"
}
hat ZimmerListe : zimmer1 zimmer2
Durch das Schlüsselwort “Erzeuge” wird die Erzeugung eines Objektes des direkt dahinter folgenden Typs (bzw. EClass aus dem Domänen-Modell) eingeleitet. Das Objekt kann einen optionalen Namen haben (wie hier zimmer1, zimmer2 usw.), welcher für Referenzen verwendet wird. Nachfolgend sind die in Klammern eingeschlossenen Eigenschaften vermerkt. Besitzt die Klasse Referenzen so sind diese am Ende jeweils mit einem “hat” eingeleitet, gefolgt von dem Namen der Referenz und einer liste der jeweiligen Objekte.
Beispiel für Selektieren/Finden von Daten
Finde Zimmer zimmer1 {
Zimmernummer = "1"
Zimmerart = Einzelzimmer
}
Finde Gast gast1 {
Vorname = ""
Nachname = ""
Email = ""
}
Finde Buchung buchung1 {
Anreisedatum = 01.07.2010
Abreisedatum = 05.07.2010
Leistungsart = Vollpension
}
hat Zimmer: zimmer1
hat Gaeste: gast1
Das Selektieren von Daten ähnelt sehr dem Erzeugen. Der Unterschied ist, dass hier die einzelnen Eigenschaften und Referenzen ähnlich einer WHERE-Clause in SQL verwendet werden, um das Objekt in der Datenbank zu selektieren und unter dem angegebenen Variablennamen zu speichern. Das Objekt kann demnach auch null sein, wenn die Ergebnismenge leer ist, oder auch eine ganze Liste von Objekte beinhalten. Wie das System damit umgehen muss, dazu ebenso im nächsten Post mehr.
Im obigen Beispiel wird eine Buchung aus der Datenbank selektiert, welche auch auf andere Objekte verweist. Diese müssen vorher ebenfalls gefunden werden. Dadurch wird sichergestellt, dass das gewünschte Zimmer und der Gast auch in der Datenbank existiert. Es sollte aber auch möglich sein, ein bereits vorher angelegtes Objekt hier als Referenz einzufügen (dazu ebenso bald mehr).
Beispiel für Assertions
// Buchung muss vorhanden sein
Nicht Null? buchung1
Eine Assertion oder Zusicherung überprüft den Zustand eines bestimmten Objektes und beendet den Test im Fehlerfall. Typische Assertions wären zum Beispiel der Vergleich zweier Objekte oder die Überprüfung, ob ein Objekt null ist.
Dies ist auch im obigen Beispiel der Fall. Greift man das Selektieren der Buchung von weiter oben auf, so wäre eine logische Folge zu überprüfen, ob die Buchung eine leere Menge ist oder nicht.
Da die DSL eher einer natürlichen Sprache ähneln soll anstatt einer Programmiersprache, werden die entsprechenden Sprachfunktionen nicht mit “AssertNotNull” oder ähnlichem bezeichnet, sondern direkt mit Wörter bzw. Wortgruppen wie “Null?”, “Nicht Null?” oder zum Beispiel “Gleich?”. Danach folgen ein oder mehrere Objekte als Parameter.
Ein vollständiges Beispiel aus der DSL werde ich im nächsten Post vorstellen. Des weiteren soll die DSL noch einfacher und leichter lesbar werden, sodass sie mehr einer natürlichen Sprache ähnelt.
Der Use Case
Um einen leichter verständlichen Einstieg in die Verwendung der DSL zu finden, verwenden wir einen einfach Use-Case aus der “Buchungssystem”-Domäne. Der Use Case ist: ein (registierter) Gast bucht ein Zimmer in einem bestimmten Hotel. Im Prinzip kann man diesen Use Case unterteilen, denn bevor der Gast ein Zimmer bucht, würde er zunächst nach einem Hotel suchen und die Verfügbarkeit eines gewünschten Zimmers prüfen. Wir gehen einfach davon aus, dass dies bereits passiert ist. Aus dem Use Case lassen sich zunächst zwei Testfälle erzeugen:
- die Buchung wird erfolgreich durchgeführt: es muss überprüft werden, ob die Buchung auch tatsächlich im System erzeugt wurde
- es wird ein bereits belegtes Zimmer gebucht: die Buchung darf nicht doppelt eingetragen werden
Der zweite Testfall sollte eigentlich nicht nötig sein, wenn vorher die Verfügbarkeit überprüft wurde. Dennoch könnte es zum Beispiel der Fall sein, dass das letzte verfügbare Zimmer belegt worden ist, bevor der Gast den Buchungsvorgang durchgeführt hat.
Es gibt eventuell noch mehr Testfälle, welche man für diesen Use Case überprüfen kann. Jedoch sollen diese beiden vorgestellten für die Beschreibung der Testerstellung ausreichen.
Eine Einschränkung im Voraus – Input-/Output-Problem
Sieht man sich die Testfälle an, so erkennt man relativ schnell eine Schwachstelle in dem System, wie wir es bisher beschrieben haben. Nicht alle Ein- und Ausgaben einer zu testenden Software lassen sich über die Datenbank realisieren. Während ich weiter oben geschrieben habe, dass es grundsätzlich um das Erzeugen von Daten in der Datenbank im Anfangszustand und das Überprüfen von Daten im Endzustand geht, so muss ich hier einige zusätzliche Bemerkungen dazu loswerden.
Stellt man sich einmal praktisch eine Buchungssystem für Hotels vor, welches über ein Web-Frontend bedient wird, so werden die Eingaben beim Buchen eines Hotelzimmers direkt über ein Userinterface eingegeben, natürlich nicht über die Datenbank. Der Gast an sich ist registriert und befindet sich in der Datenbank, ebenso wie Beherbergungsbetriebe und Zimmer.
Dasselbe Problem hat man bei bestimmten Use Cases auch beim Output der zu testenden Software. Wie soll das System nun damit umgehen? Es müsste in der Lage sein, nicht nur Zustände in der Datenbank zu beschreiben, sondern auch Ein- und Ausgabedaten.
Dabei könnten standardisierte Schnittstellen angeboten werden, welche auch die zu testende Software implementieren kann, wie die Standard-Ein- und Ausgabe, XML-Dokumente oder ähnliches. Dies ist ein relativ komplexes Thema für sich, welches wir in einem separaten Post behandeln werden. Da eine Umsetzung momentan zu umständlich wäre, beschränken wir uns auf Use-Cases, wo man tatsächlich an Hand der Datenbank Tests durchführen kann.
Speziell für den oben beschriebenen Use Case wird es so sein, dass wir vorher festgelegte Standard-Ein und Ausgaben annehmen, welche letztendlich überprüft werden. Das System betrachtet also diese Daten nicht, sie müssten stattdessen fest in der zu testenden Software implementiert sein.
Ablauf der Modellierung/Testformulierung… nächstes Mal
Auf Grund des Umfangs werde ich eine vollständige Beschreibung des Prozesses zur Beschreibung des Use Cases im nächsten Post nachliefern.
Wie sich herausgestellt hat, so gibt es noch einige Schwierigkeiten bei der Umsetzung des Systems. Dabei muss so ein Problem wie die Übergabe von Eingabeparametern und das Empfangen der Ausgabe aus dem aufgerufenen Softwaremodul zunächst abstrakt behandelt werden. Jedoch soll es dazu noch eine ausführliche Diskussion geben, wie sich diese Problemstellung in das System integrieren lässt.
Außerdem sind noch einige Arbeiten an der DSL selbst nötig, um deren Verwendung für den Benutzer möglichst einfach zu gestalten. Dennoch soll der nächste Post die Verwendung des Systems im Bezug auf das Schreiben eines Tests inklusive Domänen-Modell ausführlich beschreiben, um die grundsätzliche Arbeitsweise des Systems darzustellen.