Montag, 4. April 2011

Konzept der Akzeptanztest-DSL

Im letzten Beitrag wurde gezeigt, wie eine DSL für Akzeptanztests anhand der Anforderungen der Domäne "Forum" entwickelt wurde. In diesem Beitrag werden die Konzepte hinter diesem Ansatz einer Akzeptanztest-DSL erläutert.

Konzept der Akzeptanztest-DSL


Ein Ziel der DSL ist es, Akzeptanztests möglichst prägnant und mit möglichst wenig technischem Overhead formulieren zu können. Jeder, der mit den Akzeptanztests in Berührung kommt, soll schnell erkennen können, welche Bedingungen für erfolgreiche Durchführung der Tests nötig sind. Aus diesem Grund werden die Tests in der Form von Vor- und Nachbedingungen formuliert. Diese Art der Formulierung ist durch Use-Cases vielen bereits bekannt. Außerdem ist es dadurch möglich, die Vor- und Nachbedingungen der Use-Cases in den Akzeptanztests wiederzuverwenden.

Bei der Formulierung von Vor- und Nachbedingungen in natürlicher Sprache, werden Fachbegriffe der entsprechenden Domäne verwendet. Durch diese können Sachverhalte mit wenigen Worten präzise beschrieben werden. In der Akzeptanztest-DSL können aus demselben Grund ebenfalls Fachbegriffe verwendet werden. Damit das möglich ist, müssen alle verwendeten Fachbegriffe zunächst definiert werden. Fachbegriffe werden im Folgendem als Objekte einer Domäne betrachtet und als Domänenobjekte bezeichnet.

Domänenobjekte basieren auf anderen Domänenobjekten. „Basieren“ ist hier gleichbedeutend mit „ist ein“ (z.B. Ein Moderator ist ein Benutzer). Die Domänenobjekte der untersten Ebene sind 1 zu 1 Abbildungen von Datenbanktabellen. Jedes Domänenobjekt entspricht dabei genau einer Datenbanktabelle wobei jede Spalte der Tabelle genau einer Eigenschaft des Domänenobjektes der untersten Ebene entspricht. Dadurch wird es möglich einen SQL-Query generieren zu lassen.

Domänenobjekte bestehen aus Eigenschaften und Einschränkungen. Bei der Definition eines neuen Domänenobjektes wird das Basisobjekt als Grundlage geklont und um neue Eigenschaften und Einschränkungen erweitert. Das neue Domänenobjekt ist somit eine Spezialisierung des Basisdomänenobjektes. Ein Domänenobjekt ist nach seiner Definition nicht mehr veränderbar.

Die Eigenschaften eines Domänenobjektes sind Unterscheidungsmerkmale (z.B. Benutzer hat einen Nicknamen und einen Gruppennamen). Diese Unterscheidungsmerkmale bieten spezialisierten Domänenobjekten die Möglichkeit zur Spezialisierung. Vorgenommen werden diese Spezialisierungen durch Einschränkungen (z.B. Moderator ist ein Benutzer mit Gruppennamen = „Global Moderators“ oder Testbenutzer ist ein Benutzer mit Nicknamen = „Mustermann“).

Die Eigenschaften können neue Namen für Eigenschaften eines weiter unten in der Hierarchie liegenden Domänenobjektes sein (z.B. Benutzer basiert auf der Tabelle users. Benutzer hat die Eigenschaft Nickname. Nickname ist die Eigenschaft nick von Tabelle users). Eigenschaften können aber auch weitere Domänenobjekte mit eigenen Einschränkungen sein (z.B. Benutzer basiert auf der Tabelle users. Benutzer hat die Eigenschaft Gruppen. Gruppen sind alle Einträge aus der Tabelle groups bei denen die Eigenschaft id gleich der Eigenschaft group_id der Tabelle users ist.). Eigenschaften können aber auch Funktionen sein (z.B. Die Eigenschaft istEingeloggt ist wahr, wenn die Eigenschaft online > 0 ist, ansonsten ist sie falsch.).

Verwendung von objektorientierter Programmierung


Vergleicht man die hier verwendete Definition von Domänenobjekte mit der objektorientierten Programmierung, so kann man sich Domänenobjekte als Klassen vorstellen. Die Attribute dieser Klassen dienen zur Formulierung von Einschränkungen. An dieser Stelle werden den Attributen keine konkreten Werte zugewiesen. Das bedeutet man kann nicht sagen „x wird der Wert 3 zugewiesen“ sondern man sagt „x muss gleich 3 sein“ oder „x muss größer 5 sein“.

Diese Konzepte würden theoretisch schon ausreichen um die meisten Anforderungen einer Akzeptanztest-DSL zu erfüllen. Dem Benutzer eine Möglichkeit zur Definition von Instanzen zu geben wäre nicht unbedingt nötig. Möchte man eine bestimmte Instanz beschreiben, so muss man ein neues Domänenobjekt erstellen, welches diese ausreichend genau enschränkt. Gut eignet sich dazu z.B. die Verwendung von Eigenschaften mit Unique-Contraints bei der Formulierung der Einschränkungen (z.B. Nickname muss „Musterman“ sein). Die Vorbedingungen können bis zu einem gewissen Grad auch automatisch hergestellt werden. Wenn eine Vorbedienung z.B. besagt, dass ein Testbenutzer existieren muss und das nicht der Fall ist, dann könnte die Software selbstständig eine Instanz erzeugen, welche alle Einschränkungen eines Testbenutzers erfüllt. Nach der Testausführung könnte diese Instanz wieder gelöscht werden. Somit kann Testhygiene automatisiert stattfinden.

In der Praxis existieren allerdings sehr komplexe Datenbanken. Oft hat man nur eingeschränkte Schreibrechte. Eventuell möchte man auch eine bestehende Instanz in der Datenbank verwenden und bei dieser nur einzelne Eigenschaften ändern. Aus diesem Grund werden für Domänenobjekte die Funktionen create und find definiert, die eine neue Instanz in der Datenbank erzeugen bzw. eine bestehende Instanz aus der Datenbank sucht. Diese Instanzen können an Variablen gebunden werden. Anders als bei Domänenobjekten, können den Eigenschaften der Instanzen Werte zugewiesen werden. Bei diesen Zuweisungen werden die entsprechenden Spalten in der Datenbank aktualisiert. Instanzen haben die Funktion delete um sie aus der Datenbank zu löschen.

Damit ist die Akzeptanztest-DSL objektorientiert. Groovy bietet bereits ein Konzept für Objektorientierung. Wenn man dieses verwenden würde, müsste man es um die Möglichkeit erweitern, Einschränkungen für Attribute definieren zu können. Man bräuchte weitere Konstrukte um definieren zu können, wie sich Eigenschaften zusammen setzen. Beispielsweise würde folgendes nicht funktionieren:

class Benutzer extends smf_members {
  def Nickname = super.member_name
}

Würde man eine Instanz von der Klasse Benutzer erzeugen und dem Attribut Nickname einen Wert zuweisen wollen:

def testbenutzer = new Benutzer()
testbenutzer.Nickname = "Testbenutzer"

würde man die Information, wie sich die Eigenschaft Nickname zusammensetzt, überschreiben. Die Erzeugung von Instanzen inline-definierter Klassen, lässt sich für einen Nicht-Javaprogrammierer schwer lesen:

def poweruser = new Benutzer() {
  def where = {
    super.Beiträge.count() > 500
  }
}

Es wäre also viel technischer Overhead notwendig, wenn man das Klassenkonzept von Groovy verwenden und erweitern würde. Aus diesem Grund wird das Konzept der Domänenobjekte verwendet und um die Möglichkeit zur Erzeugung von Instanzen erweitert.

Keine Kommentare:

Kommentar veröffentlichen