Mittwoch, 30. Juni 2010

Erstellen von Akzeptanztests mit einer DSL (Schritt 3 / Teil 1)

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.

Dienstag, 22. Juni 2010

Mapping des Domänen-Modells zur Datenbank (Schritt 2)

Dieser Beitrag soll sich mit der Zusammenarbeit zwischen der fachlichen Sicht, also im Prinzip dem Domänen-Modell, und der technischen Sicht auf die Domäne, sprich der Datenbank, beschäftigen.

Da sich in diesen beiden verschiedenen Welten je nach Komplexität der Domäne einige Unterschiede ergeben können, ist es nötig, ein Mapping zwischen dem Domänen-Modell, welches der Benutzer des Systems entworfen hat und einer bereits existierenden Datenbank vorzunehmen. Dieser Vorgang müsste durch einen Entwickler vorgenommen werden, welcher Kenntnis von der Datenbank besitzt, aber auch mit dem Domänen-Modell vertraut ist.

Mapping – Zunächst nur Theorie

Im Folgenden möchte ich nicht auf eine mögliche Implementierung bzw. Umsetzung solch eines Mappings eingehen, wie es in den anderen Schritten durchaus der Fall sein wird. Aus verschiedenen Gründen werde ich mich zunächst auf die Domänen-Modellierung und den daraus resultierenden Ablauf innerhalb des Systems konzentrieren. Die nächsten Schritte (Beschreibung des Akzeptanztests in einer DSL, Codegenerierung usw.) gehören dazu und werden auch in naher Zukunft prototypisch umgesetzt. Das Mapping auf die Datenbank wird zunächst nur abstrakt behandelt.

Warum muss das Mapping sein?

Wenn man sich mit dieser Frage beschäftigt, muss man zunächst die potentiellen Unterschiede zwischen dem Domänen-Modell und dem dazugehörigen Datenbank-Schema erkennen. Zunächst ist es so, dass Entitäten einer Datenbank bereits eine Entsprechung in der Objekt-Welt haben. Dabei gilt: eine Tabelle entspricht einer Klasse. Zur Vereinfachung nehmen wir einfach an, dies wäre so. Um weiter in der Domäne “Buchungssystem” zu bleiben, gibt es also für die Klasse Beherbergungsbetrieb eine Tabelle in der Datenbank, sowie für Zimmer, Buchung usw.

Der Unterschied findet sich meistens in den Relationen zwischen den Klassen. Das Problem sind dabei weniger die One-To-Many Relationen, wie zum Beispiel zwischen Beherbergungsbetrieb und Zimmer. Diese kann ein OR-Mapper beispielsweise ganz gut allein auflösen, wobei man auch hier eventuell Hand anlegen muss, da vorhandene Felder in der Datenbank anders benannt sind, also dies im Domänen-Modell der Fall ist. Schwieriger wird es bei Many-To-Many Beziehungen. Im folgenden Bild sieht man jeweils Ausschnitte aus einmal dem Domänen-Modell und dem ERD als Grundlage für eine Datenbank:

MappingBeide01

Wir schauen uns hier die Beziehung zwischen Buchung und Zimmer an. Es handelt sich um eine Many-To-Many Beziehung – in einer Buchung können mehrere Zimmer gebucht worden sein und ein Zimmer kann selbstverständlich auch mehrere Buchungen haben.

Zunächst sieht man, dass die Beziehungen dennoch sehr unterschiedlich aussehen. Im Domänen-Modell gibt es eine eher einseitige Beziehung. Die Buchung hat eine oder mehrere Zimmer, im Bild durch einen Pfeil dargestellt. Im Modell selbst hätte die Klasse Buchung dann eine Member-Variable (vom Typ EReference), welche eine Liste von Zimmer-Objekten darstellt. Die einseitige Sichtweise, hier von Buchung aus gesehen, entsteht aus der Sichtweise des Domänenexperten: für ihn ist es hier nur interessant, dass eine Buchung eine Liste von Zimmern haben kann, dass ein Zimmer auch von mehreren Buchungen referenziert sein kann, ergibt sich von allein. (Hier kommt natürlich dazu, dass jedes Zimmer auch eine Liste seiner Buchungen haben könnte und so eine zweite, entgegengesetzte Referenz existieren könnte. Aber dies wäre hier im Beispiel nicht gewünscht. Jedoch sollte die Möglichkeit nicht unerwähnt bleiben, denn sie ist in Ecore durchaus möglich.)

In der Datenbank kann die Tabelle Buchung nicht einfach eine Liste mit Zimmer beinhalten. Hier benötigt man eine dritte, sogenannte Join-Tabelle, welche Foreign Keys auf die beiden anderen Tabellen besitzt.

Aufgaben des Mappings

Da es im Domänen-Modell keine Join-Tabelle gibt, muss irgendwo definiert sein, dass die Many-To-Many-Beziehung zwischen Zimmer und Buchung über diese Tabelle geführt wird. Daher noch einmal eine Übersicht, was das Mapping leisten muss:

  • Zuordnung je einer Tabelle zu einer Klasse
  • Zuordnung der Spalten in den Tabellen zu Attributen
  • Zuordnung der Foreign Keys zu Referenzen im Modell (bei One-To-Many)
  • Bei Many-To-Many-Beziehungen Zuordnung der Join-Tabelle

Bei den Attributen in den Klassen ist noch zu beachten, dass es auch Attribute gibt, welche auf Enumerationen verweisen. Enumerationen wären aber im DB-Schema wiederum Tabellen, also muss man hier auch auf diese verweisen und kann nicht einfach den Wert eintragen.

Problem: zu große Unterschiede Modell – Datenbank

Bis jetzt haben wir angenommen, es gibt für jede Klasse im Modell grundsätzlich eine Tabelle in der Datenbank. Was passiert aber, wenn dies nicht der Fall ist, und es in der Datenbank bestimmte Tabellen nicht gibt, oder es im Modell Klassen gibt, welche man aber nicht auf eine Tabelle abbilden kann.

Nun könnte man argumentieren, dass der ursprüngliche Datenbankentwurf sich auch in irgendeiner Weise an ein Fachklassenmodell halten musste und es so doch relativ große Übereinstimmungen geben muss. Sollte dies nicht der Fall sein, so kann man in Betracht ziehen, dass Model, welches man für seine Testfälle entwirft, als Grundlage für solch eine Datenbank heranzuziehen. (Dies könnte auch ein Use-Case für das System an sich sein. Der Domänen-Experte arbeitet von Anfang an der Software mit, indem er ein Modell bereitstellt, welches sich aus den Testfällen heraus ergeben hat und so nur die Elemente enthalten dürfte, die wichtig für die eigentliche Funktionalität sind.)

Der andere Weg wäre, das Modell entsprechend anzupassen, was aber schwierig wäre, da es hier wiederum einen größeren Dialog zwischen Domänen- und Datenbankexperten geben müsste.

Wenn es im Modell Klassen geben sollte, welche man nicht auf Tabellen abbilden kann, so sollte man in Betracht ziehen, das Modell nicht zu konkret zu gestalten. Statt dessen kann man durch Definitionen Spezialisierungen bilden, welche wiederum auf das allgemeinere Domänen-Modell abgebildet werden und so nicht mit der Datenbank in Berührung kommen.

Fazit

Ich hoffe, dieser kleine Einstieg in den relativ komplexen Schritt des Mappings ein bisschen Klarheit bringen konnte, wieso diese Funktion notwendig ist. Schon wegen diese Komplexität wird die Umsetzung des Prototyps sich darauf beschränken, keine vorhandenen Datenbank zu benutzen, sondern diese selbst aus dem Modell erstellen. Dies ist beispielsweise mit Hibernate ohne Probleme möglich.

Wie das Mapping im Genauen aussieht und welche Probleme noch entstehen können, würde man erst bei einer entsprechenden Umsetzung betrachten können. Letztendlich ist ein Austausch zwischen dem Domänen-Experten und dem Entwickler als Experte für die Datenbank sinnvoll, wenn nicht sogar dringend notwendig, um von vornherein eine Synchronität zwischen der fachlichen und der technischen Ebene herzustellen.

Mittwoch, 16. Juni 2010

Erstellen eines Domänen-Modells für Akzeptanztests (Schritt 1)

Im letzten Post habe ich kurz die verschiedenen Schritte vorgestellt, die ein System zur Akzeptanztestformulierung benötigt. Heute werde ich mich ausführlicher mit dem ersten Schritt beschäftigen – dem Erstellen eines Domänen-Modells.

Zunächst einmal wäre abzuschätzen, welche Elemente solch ein Modell benötigt und wie die Erstellung dessen ablaufen soll.

Wie müsste das Modell aufgebaut sein?

Zunächst einmal sollen alle Modelle, welche vom Benutzer erstellt werden können, die selben Elemente verwenden. Außerdem soll das System beliebige Modelle verarbeiten können. Daher benötigt es eine Art Meta-Modell, welches bereits Strukturen bietet, um eine “Objekt-Welt”, von der wir hier ausgehen, darzustellen.

Etwas grob beschrieben braucht man im Meta-Modell bestimmte Klassen, deren Eigenschaften und Beziehungen zwischen diesen Klassen. Wenn man so etwas beschreiben möchte, kommt man sehr schnell in Richtung objektorientiertes Design und UML. Dies wäre natürlich für den Zweck des Systems zu viel, deshalb benötigen wir etwas kleineres, einfacheres.

Wie soll die Erstellung eines Modells aussehen?

Der Benutzer möchte sein Modell natürlich nicht in Textform beschreiben, sondern er benötigt einen komfortablen Editor, wenn möglich, sogar in grafischer Form. Mit solch einem Editor sollte es möglich sein, einzelne Klassen mit ihren jeweiligen Eigenschaften zu erstellen. Beziehungen zwischen ihnen werden am besten durch Verbindungen und Pfeile, also rein grafisch angezeigt. Der Benutzer sollte in der Lage sein, sich die Klassen seines Modells beliebig anzuordnen, um so den gewünschten Überblick über sein Modell zu bekommen.

Vorschlag für ein Meta-Modell: Eclipse EMF (Ecore)

Das in EMF eingebaute Ecore-Metamodell ist genau das, was wir suchen. Es basiert auf Meta Object Facility (MOF), einer von der OMG eingeführten Metadaten-Architektur, bzw. auf deren Untermenge EMOF (Essential MOF). Ecore-Dateien an sich sind in einer XML-Sprache geschrieben, mit der man sich aber nicht auseinandersetzen muss. EMF bietet dazu ein Art Baumeditor an, mit welchem die Bearbeitung relativ komfortabel vonstatten geht. Besser noch ist die Bearbeitung über das Ecore-Diagramm. Dabei wird eine separate Datei angelegt, welche das Diagramm beinhaltet, Änderungen wirken sich aber auch auf die Ecore-Datei aus und anders herum. Den Ecore-Diagramm-Editor, welchen ich im folgenden noch beschreiben werden, ist aus dem GMF (Graphical Modeling Framework). Es gibt noch einen, womöglich besseren, Editor aus dem Ecore Tools Projekt, mit welchem ich mich selbst noch nicht beschäftigt habe.

Im folgenden werde ich kurz die Erstellung eines Ecore-Diagrammes erläutern. Dabei werde ich hauptsächlich auf den Aufbau des Modells eingehen, ich gehe davon aus, dass jeder Interessierte den Assistenten zum Erzeugen der Ecore-Datei findet (EMF muss installiert sein). Die Verwendung des Diagramm-Editors ist außerdem auch recht einfach.

Elemente des Ecore-Metamodells

Um ein Domänen-Modell zu erstellen, verwenden wir nur bestimmte Elemente aus dem Ecore-Metamodell.

  • EPackage: sozusagen ein Container für andere Elemente. Theoretisch kann man mehrere Packages in seinem Modell besitzen, jedoch reicht eines aus. Für den Benutzer nicht weiter wichtig, so wird es intern im System verwendet, um das Modell zu identifizieren. Das EPackage für das in diesem Post vorgestellte Beispiel bekommt den Namen “Hotel”.
  • EClass: eine Klasse aus der Domäne, kann bestimmte Eigenschaften besitzen (siehe EAttribute).
  • EAttribute: stellt eine Eigenschaft einer Klasse dar, wie beispielsweise den Namen eines Gastes. EAttributes können einen von Ecore vorgegebenen Datentyp haben, wie zum Beispiel EString, aber auch ein EEnumLiteral, also einen Enumerationswert
  • EReference: stellt eine Beziehung zwischen zwei oder mehreren Klassen her

Erstellen eines Modells

Zunächst empfiehlt es sich, den Standard Ecore Editor von EMF zu öffnen. Öffnet man den obersten Knoten (“platform:/…”), so befindet sich darin ein leeres EPackage. Dieses sollte nun einen Namen bekommen sowie ein Namespace Prefix und eine URI. Im Screenshot sieht man die beispielhaften Eingaben für die Hotel-Domäne.

Nun könnte man in diesem Editor weiterarbeiten, aber eigentlich möchten wir ja den grafischen Editor benutzen. Mit Rechtsklick auf die Ecore-Datei im Package-Explorer und der Option “Initialize ecore_diagram diagram file” erzeugt man ein entsprechendes Diagramm. Hier findet man auch den Grund, warum man zunächst das EPackage anderweitig anlegen muss: das Diagramm benötigt ein Root-Element.

Anlegen von Klassen

Der Diagramm-Editor bietet am rechten Rand eine Palette von möglichen Elementen an. Zum Erzeugen eines Domänen-Modells ist erst einmal nur EClass, EAttribute und eventuelle EEnum wichtig, außerdem noch die verschiedenen EReference-Elemente Association, Generalization und Aggregation.

Nun kann man Klassen mit ihren jeweiligen Eigenschaften anlegen. Ich habe dies im folgenden Beispiel für die Beispieldomäne durchgeführt.

EcoreDiag01

Dies ist natürlich nur ein Teil der Domäne, dieser soll aber im Moment ausreichen. Es gibt hier mehrere Enumerationen, welche von den Klassen als Datentypen für bestimmte EAttributes verwendet werden. Eine Besonderheit ist, was man im Screenshot jedoch nicht sehen kann, dass das EAttribute “Leistungsarten” der Klasse “Beherbergungsbetrieb” mehrere Werte annehmen kann (also eine Liste ist).

Erzeugen von Relationen

Relationen bzw. EReferences unterscheiden sich im eigentlichen Ecore-Modell und im Diagramm. Im Diagramm gibt es eine Verbindung zwischen den Klassen, um die Referenzen grafisch deutlich zu machen. Im eigentlichen Modell ähneln sie eher den EAttributes, mit dem Unterschied, das sie als Typ eine andere EClass besitzen.

EcoreDiag02

Im Diagramm gibt es drei verschiedenen Typen: Association, Aggregation und Generalization. Dabei wird nur bei Association und Aggregation eine EReference angelegt. Mit Generalization wird eine Vererbung ermöglicht. Dabei wird in der erbenden Klasse das ESuper Types Property gesetzt.

Eine Association ist eine einfache Referenz von einer Klasse zur anderen. So ist eine Buchung immer mit einem Gast assoziiert sowie mit einem Beherbergungsbetrieb. Eine Aggregation hingegen erzeugt eine EReference als Containment-Beziehung. Dies bedeutet, Klasse A beinhaltet sozusagen eine oder mehrere Klassen B. Bei Hotel und Zimmer lässt sich dies gut nachvollziehen.

Ebenso wie bei Attributen kann man bei Referenzen die Anzahl der Elemente angeben. Eine Buchung aus dem oberen Beispiel muss einen Gast und einen Betrieb haben, ein Betrieb aber kann 0 oder viele Zimmer haben. So lassen sich übrigens auch optionale Referenzen und Attribute erschaffen.

Weiterführende Informationen und einen Überblick über die Ecore-Modellierung findet man auch unter Eclipse Modeling Framework (EMF) – Tutorial. Hier zwar mit dem Ecore Tools Editor, das Prinzip ist aber gleich.

Ich hoffe, ich konnte die Grundlagen zum Ecore-Metamodell zum größten Teil näherbringen. Dies wird nicht der letzte Post sein, der sich mit diesem Thema beschäftigt oder einen Bezug dazu hat. Der nächste Post wird sich erst einmal mit dem nächsten Schritt zur Akzeptanztestbeschreibung beschäftigen – dem Mapping eines (Ecore-)Modells zu einer vorhandenen Datenbank.

Dienstag, 15. Juni 2010

Schritt für Schritt zum Akzeptanztest

In diesem Beitrag möchte ich die grundliegenden Schritte zur Erstellung eines Akzeptanztests vorstellen, wie sie in dem zu entwickelnden System umgesetzt werden sollen. Dies soll dazu dienen, gewissermaßen den roten Faden durch das System deutlich zu machen, bevor jeder dieser Schritte implementiert werden kann. Ich erhoffe mir dabei, somit die Kernaufgaben des Systems herauszuarbeiten sowie mögliche Probleme, die dabei entstehen könnten, einzugrenzen.

Nach dieser kurzen Einführung und Zusammenfassung werde ich jeden Schritt jeweils in einem eigenen Post genauer beschreiben. Dazu gehören jeweils die Voraussetzungen, die vorhanden sein müssen, die beteiligen Akteure, sowie die Ergebnisse, welche am Ende des jeweiligen Bearbeitungsschritts herauskommen sollen. Außerdem werden ich in jedes Mal auf die praktische Umsetzung eingehen, insofern ich bereits Ideen dazu habe. Dadurch kann man eventuell bereits erste Einblicke in die spätere Verwendung des Systems erlangen.

Dazu werde ich mir einen konkreten Use-Case in der Beispiel-Domäne “Buchungssystem” überlegen, welchen ich dann beispielhaft schrittweise in jedem Post weiter bearbeiten möchte.

Im Folgenden möchte ich die verschiedenen Schritte kurz vorstellen und erläutern.

  1. Erstellen eines Domänen-Modells

    Als Grundlage für einen Akzeptanztest muss der Benutzer des Systems, in erster Linie ein Analyst, ein Modell seiner Domäne erstellen. Es wird davon ausgegangen, dass er zumindest die Fachklassen seiner Domäne kennt, welche er auch in das Modell umsetzen muss. Er benötigt keine Kenntnis der Datenbank. Außerdem muss das Modell nicht im vollem Umfang die Domäne darstellen, sondern kann auch nur eine Teildomäne abbilden, welche für den jeweiligen Akzeptanztest notwendig ist.

  2. Mapping des Domänen-Modells zur Datenbank

    Nachdem der Analyst die fachliche Sicht auf die Domäne in Form eines Modells dargestellt hat, muss diese in die technische Sicht der Datenbank überführt werden. Diesen Schritt, das Mapping des Modells auf die Datenbank, muss ein Entwickler übernehmen, der Kenntnis von der Datenbankstruktur besitzt.

    In diesem Schritt wird es nötig sein, das Entwickler und Analyst in einen Dialog eintreten und das Domänenmodell gegebenenfalls nochmals angepasst werden muss, um ein genaues Mapping zu ermöglichen. Dies wird jedoch auch von der Komplexität der Domäne abhängig sein.

  3. Erstellen von Akzeptanztests in einer entsprechenden DSL 

    Der Analyst könnte diesen Schritt bereits vor oder parallel zum vorherigen durchführen, denn nachdem er das Domänen-Modell beschrieben hat, wäre er in der Lage dazu. Jedoch ist es angeraten, erst das Mapping auf die Datenbank abzuwarten, falls dies noch zu Änderungen im Modell führt.

    Nun hat der Benutzer verschiedene Möglichkeiten.

    1. Definitionen

      Definitionen sind Konstrukte in der DSL, welche aufbauend auf dem Domänen-Modell die Beschreibung von häufig wiederverwendbaren Standardobjekten ermöglichen. Verwendet der Benutzer für mehrere Akzeptanztests, die auf dem gleichen Modell basieren, bestimmte Objekte aus seiner Domäne, die immer wieder gleich sind, oder die zum Beispiel bestimmte Standardwerte besitzen, welche im Akzeptanztest an sich irrelevant sind, so kann er diese durch Definitionen einmal erstellen und stets wieder verwenden.

    2. Akzeptanztests

      Hier handelt es sich um die Akzeptanztests an sich. Die DSL bietet hierbei Sprachkonstrukte für die Beschreibung von Daten innerhalb eines Anfangszustandes und eines Endzustandes an. Es ist möglich, Objekte der Datentypen aus dem Modell zu erzeugen, aber auch zu überprüfen, ob sie bereits existieren. Der typische Anwendungsfall ist es, im Anfangszustand eine Reihe von Objekten zu erzeugen und im Endzustand abzufragen, ob sich bestimmte Werte geändert haben, oder ob neue Objekte hinzugekommen sind, je nach zu überprüfender Anforderung.

      An sich wird es möglich sein, Definitionen und Akzeptanztests gemischt in einer Datei zu verarbeiten, sie werden ja schließlich in derselben DSL beschrieben. Jedoch empfiehlt es sich, Definitionen und Akzeptanztests getrennt zu beschreiben, um vor allem bei vielen Akzeptanztests eine gewisse Ordnung zu erhalten.

  4. Durchführen der Code-Generierung

    Dieser Schritt erzeugt aus den in der DSL beschriebenen Daten ausführbaren Code, welcher die vorherbestimmten Aktionen auf der Datenbank ausführt. Er sollte vollautomatisch ausgeführt werten, wobei der Vorgang nur vom Benutzer angestoßen wird. Ein nachträgliches Anpassen des Codes sollte nicht nötig sein.

    Hier muss zunächst Modell-Code generiert werden, das heißt Klassen, welche derer entsprechen, die im Modell angelegt wurden. Nachher wird aus jedem Insert und Select aus der DSL für den eingesetzten OR-Mapper spezifischer Code erzeugt, welcher sich der Klassen aus dem Modell bedient. Hierbei wird das Mapping verwendet, welches Anfangs vom Benutzer bzw. Entwickler erstellt wurde.

  5. Durchführen des Akzeptanztests

    Prinzipiell werden in diesem letzten Schritt die aus dem vorherigen erzeugten Artefakte gestartet. Dabei sollte es möglich sein, kompilierte Dateien zu verwenden (z.B. Jar-Dateien) oder auch reinen Quellcode. Dieser würde dann zur Laufzeit übersetzt werden.

    Wichtig hier ist, dass die Ausführung des Tests so gestaltet sein sollte, dass die einzelnen Phasen völlig unabhängig voneinander funktionieren. Dies ist zum einen der Anfangszustand, welcher über die DSL beschrieben und nun ausgeführt werden soll, zum anderen der Endzustand. Dazwischen sollte es Platz für einen oder mehrere Funktionsaufrufe geben, welche die eigentliche Funktionalität zum Erreichen der Anforderung darstellen, für welche der Akzeptanztest geschrieben wurde.

Ich denke, dies sollte als grobe Zusammenfassung über die verschiedenen Schritte ausreichen. Weitaus ausführlicher möchte ich mich mit jedem einzelnen in jeweils einem separaten Post auseinandersetzen.

Eine Idee noch zum Schluss: eventuell kann man den vierten und fünften Schritt zusammenfassen, sodass Codegenerierung und anschließende Ausführung zusammenhängend stattfinden. Dies hätte für den Benutzer weniger Interaktionen zufolge, zumal die Codegenerierung an sich etwas ist, mit wem er im Prinzip nichts zu tun hat.

Mittwoch, 9. Juni 2010

Überblick über das Fachsprachsystem

In der letzten Woche habe ich grob die Anforderungen an ein System zur Formulierung von Akzeptanztests vorgestellt. Nun möchte ich einen allgemeinen Überblick über das System geben, wobei es sich hier nicht um eine genau Architekturbeschreibung handelt, sondern eher um eine Ablaufbeschreibung aus der Sicht des Benutzers.

Die Ideen dazu entstanden aus den verschiedenen Anforderungen, aber auch aus meinen bisher erlangten Kenntnissen über den Aufbau einer Anwendung auf Xtext- und EMF-Basis, womit das System ja auch schließlich umgesetzt werden soll.

Um den Aufbau des Systems zu erläutert, habe ich erst einmal folgendes Diagramm entwickelt, welches die wesentlichen Komponenten und deren Zusammenspiel zeigt. Dabei handelt es sich nicht um ein Standard-Diagramm, sondern um eine eigene Schöpfung, die lediglich zur groben Veranschaulichung dienen soll:

AblaufÜberblick

Projekt anlegen

Den Anfang bildet ein sogenanntes “Akzeptanztest-Projekt”, welches der Benutzer in seiner Entwicklungsumgebung (sprich Eclipse) anlegt. Theoretisch käme es hier nicht darauf an, unbedingt einen speziellen Projekttyp zu entwickeln, jedoch kann es von Vorteil sein, denn wenn der Benutzer einen Assistenten zur Verfügung hat, welche ihm ein Projekt und eventuelle vorgegebene Beispieldateien erzeugt, so fällt der Einstieg leichter. Jeweils ein Projekt sollte Akzeptanztests zu einer bestimmten Domäne beinhalten, jedoch ist dies kein Muss.

Akzeptanztest anlegen

Prinzipiell legt der Benutzer nachfolgenden für jeden zu testenden Use-Case einen Akzeptanztest an. Solch ein Akzeptanztest besteht vordergründig aus einer Textdatei, welcher der DSL, also der Fachsprache des Akzeptanztestsystems zugeordnet ist. In dieser DSL-Datei beschreibt der Benutzer den Anfangs- und Endzustandes seines Tests. Im Normalfall würde der Anfangszustand durch das Erzeugen von bestimmten Objekten, oder Daten in der Datenbank, bestimmt werden, wobei im Endzustand überprüft werden würde, wie sich diese Objekte beispielsweise verändert haben. Eine Möglichkeit wäre auch, hier Anfangs- und Endzustand jeweils in eine separate Datei zu speichern.

Ähnlich wie beim Anlegen eines Projektes profitiert der Benutzer auch hier von einer Assistenten-Funktion, da für einen Akzeptanztest nicht nur die jeweilige DSL-Datei, sondern auch andere Resourcen nötig sind, welche ihm so automatisch erzeugt werden können.

Importieren eines Domänen-Modells

Damit der Benutzer überhaupt in der Fachsprache Objekte erzeugen kann, benötigt er erst einmal ein Modell, welches die in seiner Domäne vorhandenen Begriffe beinhaltet. Dieses Modell ist das dem Eclipse Modeling Framework (EMF) zugrunde liegende Ecore-Modell. Die im Modell vorhandenen Begriffe (in Ecore Klassen u.ä.) werden prinzipiell auf eine Datenbank übertragen. Dabei ist höchstwahrscheinlich eine Transformationsschicht notwendig, da sich die im Modell vorhandenen (Fach)-Klassen-Welt von den Entitäten in der Datenbank unterscheiden kann. Der Benutzer agiert aber letztendlich nur mit dem Modell.

Des weiteren ist es wichtig, dass ein Modell aus einer bereits vorhandenen Datenbank erzeugt werden kann. Bei sehr großen Datenbanken wäre es sinnvoll, wenn nur bestimmte Tabellen aus der jeweiligen Datenbank in das Modell übernommen werden würde. So könnten für verschiedenen Akzeptanztests verschiedenen Modelle verwendet werden, welche jeweils nur die benötigte Untermenge der Domäne enthalten.

Definition von eigenen Begriffen

Der Benutzer soll die Möglichkeit haben, seine Domäne auf Basis des vorhandenen Modells zu erweitern. Dazu soll es ihm möglich sein, neue Begriffe zu definieren, sofern er dies als nötig betrachtet, beispielsweise, wenn er dies häufig wiederverwendet. Dazu erzeugt er in neues Objekt aus einer Klasse im Modell, welches bereits bestimmte vorbelegte Werte hat, oder auch eine Kombination aus mehreren dieser Objekte. Er erzeugt sozusagen eine neue, spezialisierte Klasse, welcher aber nicht im Domänen-Modell existiert, sondern nur in der DSL-Sprache. Dazu stellt dem Benutzer die DSL wiederum Sprachelemente zur Verfügung. Ihm ist dann freigestellt, ob er diese Definitionen separat definiert und sich damit eine Art Bibliothek anlegt, oder ob er sie direkt in seinem Akzeptanztest beschreibt, egal ob in der selben Datei, wie der Akzeptanztest an sich, oder in einer eigenen Datei. Die Import-Funktion der DSL macht es möglich, Inhalt auf beliebig viele Dateien zu verteilen. Dies soll durch die teilweise Überscheidung der jeweiligen Blöcke im Bild deutlich gemacht werden.

Interpretieren der DSL/Generieren von Code

Hat der Benutzer unter Verwendung des Domänen-Modells und eventueller Definitionen einen Anfangs- und Endzustand für den Akzeptanztest beschrieben, so lässt er diesen mithilfe einer Workflow-Datei (die bestenfalls ein Assistent bereits erzeugt hat) in entsprechenden Persistenz-Code umsetzen. Dabei werden mehrere Java-Klassen erzeugt, welche mittels des Teneo-Plugins aus dem Eclipse Modeling Project für Hibernate oder EclipseLink Code erzeugen, um die in den DSL-Dateien erzeugten Daten in der Datenbank unterzubringen. Teneo bedient sich hier wieder des Ecore-Modells, um die Daten in den jeweiligen Tabellen zu persistieren.

Ausführung des Tests

Mittels einer Art Skript (ich denke hier zum Beispiel an die Verwendung von Apache Ant) kann man nun die erzeugten Java-Klassen starten. Selbstverständlich muss hier zwischendurch die eigentliche Funktionalität, welche den Use-Case überhaupt erst umsetzen sollte, eingebunden werden. Dies ist jedoch nicht Aufgabe des Akzeptanztest-Systems, so wird sie als externes Artefakt in das Skript eingebunden.

Hier wird es sich zeigen, ob diese Art des Aufrufs praktikabel ist, oder ob sich hier eine andere Möglichkeit bietet. Statt das der Benutzer das Skript aufrufen muss, könnte dieses auch automatisiert bei Regressionstest und ähnlichem ausgeführt werden.

Zusammenfassung und Ausblick

Ich denke, der vorgestellte Aufbau bzw. Ablauf des Systems, welches ich mir vorgenommen habe, umzusetzen (zumindest zu einem großen Teil :-) ), ist im Großen und Ganzen umsetzbar und auch brauchbar für einen Fachbereichsmitarbeiter als Benutzer. Viele Dinge sollte ihm einfach von Assistenten abgenommen werden, nicht zuletzt, um den Überblick zu bewahren, gerade wenn es viele Akzeptanztests in einem Projekt gibt. Gerade hier muss auch jeder Test eindeutig identifizierbar sein und in einer geordneten Struktur abgelegt werden. Deshalb ist es von Vorteil, dem Benutzer einige Vorgaben zu machen.

Eine große Herausforderung wird es, die Domänen-Modelle, insbesondere das Ecore-Modell an sich, aus einer bereits vorhandenen Datenbank zu erzeugen. Momentan ist mir noch kein Weg bekannt, um dieses zu erreichen. Hier wird vielleicht eine separate Erstellung des Modells mit dazugehörigem Mapping auf die Datenbank und der damit verbundenen Kooperation zwischen Fachbereichsmitarbeiter und Entwickler nicht zu vermeiden sein. Jedoch stellt dies auch eine große Möglichkeit dar, denn die Funktion, einzelne (Unter-)Modelle der Domäne zu erzeugen, sollte in dem System nicht fehlen. Einer meiner nächsten Posts wird sich sicher damit beschäftigen.

Donnerstag, 3. Juni 2010

Anforderungen an ein Fachsprachsystem zur Formulierung von Akzeptanztests

In den letzten Posts habe ich eine Einführung in Xtext und Co. gegeben, welche als Technologie dazu dienen sollen, das oben genannte System umzusetzen. Nun ist es an der Zeit, einmal kurz von der Praxis wegzugehen und festzulegen, wie das System aussehen soll, d.h. welche Funktionen es besitzen soll und wie es in seiner Verwendung aussehen soll.

Dazu sind erst einmal die funktionalen Anforderungen an das System wichtig. Der Kern des Systems ist es natürlich, Akzeptanztests zu erstellen. Da wir uns hier auf datenbankgestützte Anwendungen beziehen, muss ein Akzeptanztest stets sicherstellen, dass eine bestimmte Funktion einer zu entwickelnden Software einen bestimmten Datensatz in der Datenbank ändert oder erstellt. Um dies zu überprüfen, muss der Benutzer in der Lage sein, einen Anfangszustand in der Datenbank zu beschreiben, indem er zum Beispiel bestimmte Daten erzeugt, und außerdem einen Endzustand zu bestimmen, welcher am Ende des Tests überprüft wird.

Zum Erstellen der Akzeptanztests verwendet der Benutzer eine Fachsprache, welche die nötigen Funktionen mitbringen und prinzipiell mit jeder Datenbank funktionieren muss. Um dies umzusetzen, muss die Fachsprache ein Meta-Modell verwenden, welches Datenbank-ähnliche Strukturen bietet. Für jede Datenbank muss ein konkretes (Domänen-)Modell existieren, welches auf dem Meta-Modell basiert. So sollte es möglich sein, das Akzeptanztest-System für verschiedene Domänen zu benutzen.

Nachfolgend habe ich eine Liste mit funktionalen Anforderungen an das System ausgearbeitet. Diese habe ich in Gruppen eingeteilt, dabei wird zwischen den Funktionen unterschieden, welchem unmittelbar vom Benutzer in Anspruch genommen werden und denen, die das System an sich sonst noch anbieten muss.

Funktionen bezogen auf den Benutzer/die DSL

  • der Benutzer kann Anfangs- und Endzustand der Daten für einen Akzeptanztest in einer geeigneten DSL beschreiben
  • die DSL muss Möglichkeiten bieten, Daten zu erzeugen, auf Vorhandensein zu überprüfen und ggf. zu löschen
  • der Benutzer muss ein Modell auf Basis der Datenbank erzeugen bzw. selbst erstellen können, welches als Grundlage für die Beschreibung der Testdaten dient
  • der Benutzer muss in der Lage sein, basierend auf Entitäten aus dem jeweiligen Modell eigene Fachliche Begriffe zu definieren, welche er in der DSL verwenden kann
  • erstellte Akzeptanztests sollten aufeinander aufbauen können, d.h. eine DSL-Datei kann eine andere importieren und deren Definitionen weiterverwenden
  • der Benutzer sollte in der Lage sein, die Akzeptanztests aus dem Programm aufzurufen

Automatische Funktionen des Systems

  • das System erstellt automatisch auf dem Modell basierende Persistenz-Klassen aus dem formulierten Akzeptanztest hinaus
  • formulierte Akzeptanztests müssen in Artefakte verwandelt und in einer automatisierten Umgebung nutzbar gemacht werden
  • möglichst viele Funktionen sollten dem Benutzer durch Assistenten u.Ä. abgenommen werden

Erweiterte Funktionen

  • das Importieren der Datenbankstruktur in eine Modell muss in Bezug auf sehr große Datenbanken auf bestimmte Schemas/einzelne Tabellen beschränkbar sein
  • das System muss in der Lage sein, die technische Sicht der Datenbank von der fachlichen Sicht zu trennen (ggf. über eine Transformation im Modell)

Daraus ergeben sich auch einige nichtfunktionale Anforderungen, auf welche ich jetzt aber nicht gesondert eingehen möchte. Zu erwähnen wäre hierbei jedoch beispielsweise die leichte Benutzbarkeit des Systems, welche mit der Benutzerzielgruppe des Fachbereichsmitarbeiters bzw. Analysten einhergeht.

Basierend auf diesen Anforderungen werde ich beginnen, eine Architektur bzw. zunächst einmal einen Ablaufplan für die Verwendung und Funktionalität des Systems zu erarbeiten. Dies wird in den nächsten Beiträgen Gegenstand der Betrachtung sein.