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.

Samstag, 29. Mai 2010

Workflow eines Xtext-Projektes

Beim letzten Mal haben wir uns mit der Erstellung einer Sprache in Xtext beschäftigt, genauer gesagt mit der Verwendung der Xtext Grammatik zum Erstellen eines eigenen Modells, im Grunde genommen also mit dem Kern der DSL.

In diesem Beitrag würde ich gern den Workflow beschreiben, welchen das Xtext-Projekt durchläuft. Ich hoffe, damit erst einmal ein grundsätzliches Verständnis für meine Pläne zur Umsetzung eines Akzeptanztest-Systems, welche ich in den kommenden Posts vorstellen möchte, aufbauen zu können. In dem System werden sich einige Elemente wiederfinden, denen man auch in Xtext begegnet.

Erzeugung der Artefakte mit der Modeling Workflow Engine (MWE)

Sobald man ein Xtext-Projekt angelegt hat, so findet sich neben der Grammar-Datei mit der Dateiendung xtext eine Workflowdatei mit dem Namen Generate[Sprachname].mwe:

XtextWorkflowDSLProject

Im Bild sieht man den Package-Explorer mit der Ansicht des auch schon im letzten Post verwendeten Projekts. Neben der Workflow-Datei gibt es noch eine Properties-Datei mit einigen projektspezifischen Eigenschaften, welche der Workflow verwendet.

Der Inhalt solch eines Workflows soll uns an dieser Stelle nicht interessieren, dies würde den Umfang des Posts sprengen. Was man wissen muss, ist, dass ein MWE-Workflow einen sequenziellen Aufruf von verschiedenen Komponenten zur modellgetriebenen Entwicklung ermöglicht, daher "Workflow". Das Ganze geschieht in einer XML-basierten deklarativen Notation, außerdem ist es möglich, eigene Komponenten zu schreiben. Mehr dazu findet man auf der MWE-Seite im Eclipsepedia. Ich werde auf diese Technologie an einem anderen Zeitpunkt genauer eingehen.

Führt man den Workflow aus (Rechtsklick –> Run as –> MWE Workflow), so erzeugt Xtext alle benötigten Artefakte, vom Ecore-Modell über das EMF-GenModel (welches wiederum den Java-Code erzeugt) bis hin zu den UI-spezifischen Komponenten wie Editor und Contentassistance.

Diesen Vorgang muss man jedes mal wiederholen, sobald man etwas an der Grammatik geändert hat.

Generiertes UI-Plugin/Projekt

Das vom Xtext-Projekt Assistenten erzeugte Projekt mit der Endung ui wird erst nach dem ersten Durchlauf des Workflows mit Inhalten gefüllt:

XtextWorkflowUIProject

Hier hat man die Möglichkeit, den Proposalprovider anzupassen, einen Labelprovider zu erstellen etc., also sein Plugin für die Sprache anzupassen. In meinem Projekt für die Erstellung eines Akzeptanztest-Systems wird hier ein Wizard hinzukommen, der die Erstellung des Projektes oder auch von Akzeptanztests unterstützt.

Über die plugin.xml-Datei kann man das Plugin starten und den automatisch erstellten Editor benutzen. Man erstellt dann lediglich ein neues, leeres Projekt und eine Datei mit der vorher festgelegten Endung:

XtextWorkflowNewProject

Hier zeigt sich schon eine hervorragende Möglichkeit zur Erweiterung des Plugins: das Akzeptanztest-System könnte durch einen Assistenten die Möglichkeit zum automatischen Erstellen einer DSL-Datei mit der richtigen Endung zur Verfügung stellen.

Generiertes Generator-Projekt (optional)

Zusätzlich zu dem DSL-Projekt und dem UI-Projekt kann man sich ein Generator-Projekt (endet mit .generator) angelegen lassen. Im Xtext-Projekt-Wizard muss man dazu an der entsprechenden Stelle ein Häkchen setzen.

Das erzeugte, eigenständige Projekt dient dazu, aus dem durch Xtext erzeugten Modell Code zu generieren. Damit ist nicht nur Java-Code gemeint, sondern prinzipiell irgendein Code, dies könnten zum Beispiel ein XML-Dokument sein oder SQL-Statements, je nachdem, welchem Zweck das zugrundeliegende Modell dient.

Um dieses Ziel umzusetzen, bedient man sich hier der Codegenerierungssprache Xpand. Der Projekt-Wizard erzeugt im Generator-Projekt bereits einige Standard-Dateien, wie eine DSL-Datei der Xtext-Beispiel-DSL "Entities" und ein Xpand-Template, welches auf diesem Modell arbeitet:

XtextWorkflowGenProject

Des weiteren gibt es hier ein Xtend-Datei, welche von dem Xpand-Template eingebunden wird. Mit Xtend kann man Erweiterungen zu seinem Model schreiben, ohne das Modell zu ändern, ebenso wie eigens definierte Operationen, welche das Modell benutzen. Genauer möchte ich darauf nicht eingehen, da es für meine Zwecke nicht so wichtig ist. Dennoch sollte es nicht unerwähnt bleiben.

Die Beispieldateien würde man löschen und gegen das eigene Modell ersetzen. Was man nun mit Xpand machen kann, werde ich in einem neuen Post zeigen.

Aufgerufen wird Xpand wiederum durch eine Workflow-Datei, welche sich im Projekt befindet. Insgesamt hat dies jedoch wenig Sinn, wenn es nicht aus Plugin aus aufgerufen wird. Wie würde man also dazu vorgehen?

Man startet das Plugin über das UI-Projekt und legt in der neuen Workbench ein Projekt an. Dieses Projekt muss als Dependency das Generator-Plugin besitzen, um auf den Generator und die Xpand-Templates zugreifen zu können. Der im Generator vorhandene Workflow kann nun aus einem eigens erstellten Workflow aufgerufen werden. Dieses Beispiel stammt von der Seite von Peter Friese:

<workflow>
<cartridge file="workflow/EntityGenerator.mwe" model="classpath:/model/MyModel.entity"/>
</workflow>

Der Generator wird über den Workflow aufgerufen und verwendet dann die in der aktuellen Workbench erzeugte DSL-Datei, anstatt die, welchem im Generator-Projekt erzeugt wurde.

In dem oben genannten Artikel von Peter Friese findet man noch mehr Informationen zur Code-Generierung mit Xpand.

Src gegen Src-gen

Jedes der angelegten Projekte verfügt über zwei verschiedenen Source-Ordner. Einmal src, wo sich die Dateien befinden, die der Entwickler selbst ändert. Dann gibt es den Ordner src-gen, in welchem sich fast ausschließlich von Xtext erstellter Code befindet, wie zum Beispiel die Java-Klassen zum erzeugten Modell. Diese Dateien sollte man möglichst nicht verändern, da sie vom Xtext-Generator eh wieder überschrieben werden. Ebenso sollte man eigene Klassen und sonstige Dateien wie eigene Workflows stets in den src-Ordner legen.

Validation

Auch wenn man seine Xtext-Grammatik optimal angelegt hat, so kann man nicht immer vorhindern, dass die DSL eventuell an bestimmten Stellen Inhalte akzeptiert, welche man da eigentlich nicht haben wollte. Auch durch Referenzen kann dieses Problem oft nicht gelöst werden.

Die Lösung bietet ein von Xtext angebotenes Validator-Fragment, welches dem Workflow zur Generierung der Artefakte hinzugefügt wird. Letztendlich beschreibt man in einer Java-Klasse Methoden, welche Überprüfungen von einzelnen Regeln der Grammatik anstellen. Was man genau in diesen Methoden überprüft, ist jedem selbst überlassen.

Genaueres dazu wird es in einem kommenden Post von mir geben, da ich schon mehrfach Gebrauch von dieser Funktion gemacht habe. Inzwischen finden sich Informationen darüber i, Xtext User Guide in Chapter 5 – Unterpunkt "Validation".

Fortsetzung folgt…

In meinem nächsten Post habe ich vor, bereits einmal die prinzipiellen Ablauf, welchen ich mir für das Akzeptanztest-System vorgestellt habe, zu erläutern. Weitere Posts zu Validation und anderen Details möchte ich dann bereits an diesem System demonstrieren.

Mittwoch, 26. Mai 2010

Verwendung von XText

Mit diesem Post (und den nächsten) möchte ich ein Gefühl für die Verwendung von XText vermitteln. Dabei werde ich erst einmal auf die Erstellung der Grammatik eingehen, später auf den Workflow zur Erzeugung der Artefakte und des Plugins. Dies wird eine Basis bieten, um später die Konzepte für den Aufbau des Akzeptanztestsystems zu erläutern.

Dennoch wird es hier keine vollständige Anleitung für XText geben. Die Details kann man in der User Doku zu XText nachlesen. Hier findet man alle Möglichkeiten der Grammatik von XText und Informationen zu weiteren Konzepten, welche auch hier kurz angeschnitten werden sollen.

Außerdem empfehle ich für Beispiele, aber auch spezielle Thematiken rund um XText die Blogs von Sven Efftinge und Peter Friese. Da erfährt man, wie man zum Beispiel vorhandene Ecore-Modelle in XText importiert oder wie man mithilfe von XPand aus einer DSL Code generiert, was auch mir mittlerweile sehr geholfen hat.

Erstellung der Grammatik

Zunächst einmal habe ich eine einfache Grammatik für eine DSL zur Beschreibung von Hotels erstellt. Es ist zwar sehr einfach gehalten, sollte aber deshalb auch leicht nachzuvollziehen sein. Nachdem man ein neues XText Projekt angelegt hat (siehe XText User Guide), kann man die automatisch erstellte Grammatik folgendermaßen abändern:

grammar org.xtext.example.Testdsl with org.eclipse.xtext.common.Terminals

generate testdsl "http://www.xtext.org/example/Testdsl"

Model :
(hotels+=Hotel)*
(zimmer+=Zimmer)*
(gaeste+=Gast)*;

Hotel:
'Hotel' name=ID
'Zimmer' '{' (zimmer+=[Zimmer])+ '}'
'Gaeste' '{' (gaeste+=[Gast])+ '}';

Zimmer:
Einzelzimmer | Suite;

Einzelzimmer:
'Einzel' name=ID;

Suite:
'Suite' name=ID;

Gast:
'Gast' name=ID
'Vorname' vorname=STRING
'Nachname' nachname=STRING;

Die Grammatik beginnt mit ihrem Namen und dem Einbinden der von XText bereitgestellten Terminals. Diese Terminals, wie im Beispiel zu sehen ID, STRING und weiterhin INT sowie andere sind in einer eigenen Grammatik definiert, welche man meistens in seiner Grammatik mit verwenden möchte. Nach dem selben Prinzip lassen sich auch beliebige andere Grammatiken einbinden.

Das Schlüsselwort generate stellt den Ausgangspunkt eines wichtigen Prinzips von XText dar. Hier wird der Name des aus der Grammatik zu erstellenden Ecore-Modells angegeben. In Ecore wird das durch die DSL beschriebene Modell festgehalten, woraufhin man dieses mit den Möglichkeiten des Eclipse Modeling Frameworks weiter bearbeiten kann. Es ist auch möglich, auf die Generierung zu verzichten und stattdessen ein Modell zu importieren, bzw. eine Kombination davon. Darauf wollen wir an dieser Stelle erst einmal verzichten.

Mit dem Hintergedanken an das Modell, welches schließlich erzeugt wird, kann man nun mit Hilfe der Grammatik verschiedenen Regeln beschreiben, welche das Modell beschreiben, aber auch das Aussehen der DSL. Die EBNF-ähnliche Form der Grammatik (EBNF – Erweiterte Backus-Naur-Form) kann man relativ einfach nachvollziehen.

Die Regel Hotel zeigt grundlegende Funktionen der Grammatik:

Hotel:
'Hotel' name=ID
'Zimmer' '{' (zimmer+=[Zimmer])+ '}'
'Gaeste' '{' (gaeste+=[Gast])+ '}';

Mit Anführungszeichen eingeschlossenen Wörter gelten als Schlüsselwörter in der DSL, welche fest verankert sind und immer an ihrem vorbestimmten Platz vorkommen müssen. Sind sind lediglich dazu da, der DSL eine Struktur zu geben und sie leichter lesbar zu machen. In das Modell werden sie nicht übernommen.

Eine Regel in der DSL beschreibt meist eine Klasse im Modell. Jede Klasse bekommt bestimmte Eigenschaften oder auch Attribute, welche jeweils einen bestimmten Wert haben. In XText spricht man hier auch von Referenzen, welche die einzelnen Regeln im Syntaxbaum miteinander verbinden. Hotel besitzt ein Attribut (um in der Objektwelt zu bleiben) mit dem Namen name, welchem ein bestimmter Wert zugewiesen wird. Dieser wird hier durch die Terminal-Regel ID gebildet. Beim Parsen wird später der Inhalt dieser Regel hinter dem Schlüsselwort Hotel eingefügt, wobei es sich bei ID um eine Kombination aus Ziffern und Buchstaben handeln kann. Ein anderes Beispiel sind vorname und nachname in der Regel Gast.

Möchte man eine Liste erstellen, das heißt, kann das Attribut mehr als einen Wert annehmen, so wird dabei das = durch += ersetzt. Diesen “add”-Operator kennt man aus einigen Programmiersprachen. Ein Beispiel sieht man in der obersten Regel Model:

Model :
(hotels+=Hotel)*

Hier wird zum Beispiel beschrieben, dass die Regel Hotel mehrmals hintereinander existieren kann.

Kardinalität von Elementen

XText bietet mehrere Optionen an, um die Kardinalität, sprich die Anzahl der Elemente anzugeben:

  • keine Angabe – genau ein mal
  • ? – Maximal einmal, oder nicht vorhanden (kennzeichnet Optionalität)
  • + – bei mehreren Elementen, 1 mal oder mehr
  • * – bei mehreren Elementen, 0 oder mehr

Möchte man beispielsweise name aus dem oberen Beispiel optional werden lassen, so würde man es so beschreiben:

'Hotel' (name=ID)?

Beispiele für Listen mit mindestens einem oder mindestens 0 Elementen findet man in der Beispiel-DSL mehrfach. So muss ein Hotel mindestens ein Zimmer besitzen, jedoch kann das Modell auch gar keine Hotels oder Gäste besitzen.

Cross References

Bis jetzt haben wir uns nur mit dem Zuweisen von Regeln beschäftigt. An der entsprechenden Stelle wird die Regel vom Parser eingesetzt. Man kann jedoch auch Objekte durch Regeln an einem Ort beschreiben und diese dann wieder an einem anderen Ort wiederverwenden. Ein Beispiel:

'Hotel' name=ID
'Zimmer' '{' (zimmer+=Zimmer)+ '}'

Würde man die Regel in dieser Weise ausführen, so müsste man die Zimmer direkt in die geschweiften Klammern des Hotels schreiben, also:

Hotel hotel1
Zimmer { Einzel ... }

Geht man wieder zu der oben verwendeten Schreibweise zurück, so kann man an dieser Stelle Referenzen verwenden. Diese werden dadurch gekennzeichnet, dass man die entsprechende Regel in eckige Klammern setzt. Dies bedeutet dann so viel wie: hier wird ein Objekt/ eine Instanz vom Typ X eingesetzt.

'Hotel' name=ID
'Zimmer' '{' (zimmer+=[Zimmer])+ '}'

Nun würde man vorher ein Zimmer definieren, ihm einen Namen geben und diese Referenz dann verwenden.

Suite zimmmer1

Hotel hotel1
Zimmer { zimmer1 }

Wichtig hierbei ist: um eine Referenz wieder zu finden, muss die entsprechende Regel ein Attribut mit dem Namen name besitzen, so wie es schon mehrfach in der Beispielgrammatik vorkommt. Ich denke, dies hängt direkt mit dem Ecore-Modell zusammen. Die meisten Elemente des Ecore Meta-Modells erben von einer Klasse namens ENamedElement, welche für alle Elemente ein Attribut name definiert und sie so identifizierbar macht. Alle anderen Attribute, die nicht zur Referenzierung verwendet werden, kann man natürlich beliebig benennen.

Verzweigungen / Alternativen

Oben in der Grammatik gibt es eine Verzweigung bei Zimmer, wobei die Regel Zimmer zwei Alternativen bietet: Einzelzimmer oder Suite.

Zimmer:
Einzelzimmer | Suite;

Ob diese Konstellation so sinnvoll ist oder nicht sei dahingestellt, jedoch lässt sich hier gut der Sinn solch einer Verzweigung darstellen. Sieht man das Ganze wieder im Hinblick auf das später erzeugte Modell, so erzeugt man hier eine Art der Vererbung. Zimmer ist die Superklasse und Einzelzimmer bzw. Suite sind die spezialisierten Klassen. In unserem Beispiel macht dies nicht so ganz Sinn, denn beide fügen keinerlei Spezialität hinzu sondern besitzen nur das gleiche Attribut name.

Hierbei sieht man aber eine Besonderheit im Aufbau solch einer Hierarchie. Alle gleichen Attribute, das heißt, eigentliche solche, die in die Superklasse kommen würden, müssen hier jeweils in den Regeln aller Kindklassen vorkommen. Die Regel Zimmer ist hier nur dazu da, die Verzweigung zu bestimmen. Im später erzeugten Modell werden dann alle gleichen Attribute weiter oben angesiedelt, wie man im folgenden Abschnitt sehen wird.

Eine weitere Einsatzmöglichkeit solcher Verzweigungen ist, den Ablauf der Sprache etwas beliebiger zu gestalten, sodass man beispielsweise Hotels, Zimmer und Gäste nicht in der genauen Reihenfolge beschreiben muss, sondern diese durch eine Verzweigung einer gemeinsamen Superklasse zuordnet, wodurch es möglich wird, die Reihenfolge völlig beliebig zu gestalten.

Das Modell hinter der Grammatik

Ohne momentan den Workflow zur Erstellung des Modells zu betrachten (das kommt beim nächsten Mal), möchte ich gern einmal so ein Modell vorstellen, und zwar genau das, welches durch die oben beschriebene Grammatik erstellt werden würde.

Ecore-Modell für das Beispiel

In dem Ecore-Modell wird für jede Liste eine EReference (gekennzeichnet durch den Pfeil am Symbol) angelegt, für jedes einzelne Attribut ein EAttribute. Wie man hier sieht, wird die Verzweigung bei Zimmer dadurch aufgelöst, dass hier Unterklassen gebildet werden. Alle gemeinsamen Attribute kommen dabei in die Superklasse, was in unserem Fall nur name ist.

Dies ist noch ein sehr einfaches Beispiel. Wir werden sehen, dass im Laufe der Entwicklung des AKzeptanztest-Systems ein sehr viel komplizierteres Modell herauskommen wird, vor allem da ein Modell nötig sein wird, welches nicht nur eine Domäne wie hier “Hotel”, sondern beliebig viele unterstützen muss.

Ein konkretes Beispiel zum Abschluss

Zum Schluss folgt ein konkreter Text in der oben vorgestellen DSL:

Hotel hotel1
Zimmer { zimmer1 zimmer2 }
Gaeste { gastMuster }

Einzel zimmer1
Suite zimmer2

Gast gastMuster
Vorname "Max" Nachname "Mustermann"

Hier erhält man nun einen Eindruck, was aus der oben beschriebenen DSL werden kann. Man beachte die Reihenfolge insbesondere der Schlüsselwörter und die Verwendung der Referenzen. Im Laufe der Zeit werde ich in diesem Blog immer mal wieder einzelne Details von XText vorstellen, welche zur Umsetzung des Akzeptanztest-Systems nötig sind.

Im nächsten Post habe ich vor, die Erstellung eine DSL aus der Sicht des Workflows zu betrachten, welchen man durchführen muss, um aus der Grammatik ein Modell zu machen und ein Plugin zu erzeugen, mit welchem man dann einen konkreten Text in der DSL schreiben kann.

Donnerstag, 20. Mai 2010

Installation von XText

Zur Installation von XText muss man eigentlich nicht viele Worte verlieren. Auf der Website von itemis gibt es eine vorgefertigte Eclipse Distribution inklusive XText und allen Abhängigkeiten.

Wer sich die Plugins manuell in sein bereits vorhandenes Eclipse einbauen möchte, dem möchte ich momentan davon abraten. Die Links zu den Plugins befinden sich auf der Downloadseite vom Textual Modeling Framework, ebenso die Adresse der Update-Site (diesen Weg sollte man sowieso stets vorziehen). Auch nachdem ich XText über den Update-Manager eines neu aufgesetzten Standard-Eclipse installiert hatte, erschien folgende Fehlermeldung beim Anlegen eines neuen XText Projekts:

Fehler beim Anlegen eines neuen XText Projects

Hierzu gab es keine Erklärung. Alle nötigen Plugins waren vorhanden, wie ein Vergleich mit der vorgefertigten Distribution zeigte. Deshalb möchte ich auch dazu raten, diese zu verwenden.

Ich werde der Sache weiter nachgehen, vielleicht kann ich dann hier eine Lösung dazu anbieten. Im nächsten Post werde ich kurz demonstrieren, wie die Erstellung einer DSL in XText abläuft, bevor sich demnächst alles um die Implementierung des Akzeptanztestsystems drehen wird.

Anmerkung: Alle hier gemachten Angaben beziehen sich auf Eclipse 3.5 Galileo und Version 0.7.2 von XText.

Mittwoch, 19. Mai 2010

Warum XText?

Für die Verwendung des XText-Frameworks zur Umsetzung des Fachsprachsystems gibt es mehrere gute Gründe. Einerseits lassen sich damit externe DSLs (Domain Specific Languages) erstellen. Diese können eine völlig neu definierte Syntax besitzen, was von Vorteil ist, wenn so eine Sprache von einem Domänen-Experten benutzt werden soll und nicht von einem Softwareentwickler. Im Gegensatz dazu ist man bei einer internen DSL von deren Wirtsprache abhängig. Beim Testen von verschiedenen Ansätzen hat sich XText als sehr geeignet für das System herausgestellt, wodurch es dabei geblieben ist. Deshalb wird hier nun nicht weiter auf interne DSLs eingegangen.

Wer mehr über interne und externe DSLs erfahren möchte, dem empfehle ich die Homepage von Martin Fowler. Hier findet man einen kurzen Artikel über DSLs; mehr Informationen findet man in seinem Work-In-Progress Buch "Domain Specific Languages", welches auch über die Seite erreichbar ist.

Das Einsatzgebiet das Fachsprachsystems für Akzeptanztests sind datenbankbasierte Applikationen. Da XText sehr eng mit dem Eclipse Modeling Framework (EMF) integriert ist, kann man die gesamte damit verbundene Technologie zur modellgetriebenen Entwicklung mit verwenden, von der Erstellung eines Domänen-Modells bis hin zur Persistenz. Diese Schritte werden nach und nach in diesem Blog dokumentiert werden.

Davon abgesehen ist die Verwendung einer Plattform wie Eclipse im Allgemeinen von Vorteil. Hier besteht die Möglichkeit, ein Plugin oder eine RCP-Anwendung zu erstellen, mit welchem der Domänen-Experte Tests formulieren kann und der Softwareentwickler diese in seiner Arbeitsumgebung verwenden kann bzw. die Funktionalität dazu implementiert.


Zusammenfassung

Weshalb verwende ich XText:
  • Externe DSL mit frei definierbarer Syntax
  • Anbindung an Eclipse Modeling Framework
  • Nutzung der Eclipse Plattform
Alles zu XText findet man auf dessen Projektseite auf Eclipse.org. Im nächsten Post möchte ich die Installation von XText etwas genauer beschreiben.