Wissen ist das einzige Gut,
das sich vermehrt, wenn man es teilt.

 
 

Fixtures mit Grails

Unter Fixtures versteht man Daten, die man insbesondere während der Applikationsentwicklung zum Testen benutzt. Zumeist handelt es sich dabei um Objekte von Domain-Klassen. Die Notwendigkeit solcher Testdaten wird spätestens dann ersichtlich, wenn innerhalb von Tests jene Domain-Daten als verfügbar vorausgesetzt werden, etwa um sie zu lesen, gezielt zu verändern und anschließend Zusicherungen (assertions) auf ihnen zu machen.

 

Beispielsweise möchte man in einem Pizza-Webshop unterschiedliche Pizzen bereithalten, um Bestellungen im Rahmen von Tests zu simulieren.

 

Grails unterstützt die Entwicklung dabei in mehrerlei Hinsicht:

Zunächst lassen sich für die verschiedenen Umgebungen (Entwicklung, Test, Produktion) unterschiedliche Datenquellen ankonfigurieren. Mit Grails 2.0 ist standardmäßig die H2-Datenbank mit der "create-drop"-Option vorhanden, die bewirkt, dass die Datenbank nach jedem Test in demselben, definierten Vorstand bereitsteht.

 

Nun muss die Datenquelle vor der Ausführung von Tests noch mit Daten befüllt werden - und hier kommen die eingangs erwähnten Fixtures ins Spiel:

Grails bietet über das FixturesPlugin eine sehr elegante Möglichkeit, auf einfache Weise Testdaten zu erstellen. Selbst wenn Groovy schon prägnant ist, bietet das Fixtures-Plugin eine noch knappere DSL-Notation an, die wir uns gleich ansehen werden.

 

Schauen wir vorab an, wie wir ein paar Fixtures erstellen können. Für unsere Zwecke wollen wir ein paar Produkte und User unseres imaginären Webshops als Fixtures erstellen. Ein Produkt (wir verkaufen Pizzen, die Zutaten enthalten) soll dabei einen Preis und eine Bezeichnung haben, ein User einen Namen und eine Email-Adresse.

 

Für wenige Daten kann man dies in der BootStrap.groovy etwa so vornehmen:

 

class BootStrap {
    // ....
    def fixtureLoader
    def init = { ServletContext sC ->
 
        if (GrailsUtil.environment == GrailsApplication.ENV_DEVELOPMENT) { // nicht hübsch aber wirkungsvoll...
 
            fixtureLoader.build {
            // Zutaten
                tomatoes(demo.shop.Ingredient) {
                    name = "Tomaten"
                    price = 0.29
                }
                mozzarella(demo.shop.Ingredient) {
                    name = "Mozzarella"
                    price = 0.49
                }
 
                // Pizza
                margeritha(demo.shop.Pizza) {
                    name = "Margherita"
                    description = "Der Klassiker"
                    price = 4.00
                    image = margerithaImage
                    ingredients = [tomatoes, mozzarella]
                }
            }
        }
    }
} 

In der Regel möchte man jedoch all seine Fixtures in Grails' Bootstrap.groovy erfassen, noch sollen sie alle untereinander stehen. Daher bietet es sich an, diese in separate Dateien auszulagern - etwa, pro Domainklasse eine. Auch für diesen Zweck bietet das Fixture-Plugin erfreulicherweise einfache Mittel.

Im Projekt-Verzeichnis wird mit Installation des Plugins ein "fixtures"-Verzeichnis angelegt. Um der Konvention zu folgen, sollten wir darin noch entsprechend der Package-Namen unserer Domain-Klassen ebenfalls eine Verzeichnisstruktur anlegen. Für unsere Zwecke z.B. "demo/ shop " .

Nun legen wir darin pro Domain-Klasse jeweils eine Groovy-Datei an - für die Zutaten etwa Ingredients.groovy. Zu beachten ist, dass es nicht notwendig ist, eine Klasse zu definieren. Die Fixtures werden lediglich in die gleichnamige Closure eingebunden:

// demo/shop/Ingredients.groovy 
fixture {
    tomatoes(demo.shop.Ingredient) {
        name = "Tomaten"
    }
    mozzarella(demo.shop.Ingredient) {
        name = "Mozzarella"
    }
}

Eine Pizza besteht aus unterschiedlichen Zutaten. Wenn in der nachfolgenden Fixture-Deklaration der Pizzen Bezug auf Zutaten genommen werden soll, müssen diese Abhängigkeiten beibehalten werden. Dies erreichen wir einerseits durch ein "include", andererseits durch die ref-Methode, die genauso funktioniert wie bei den SpringBeans. Das folgende Beispiel verdeutlicht dies sicher am besten:

// demo/shop/Pizzas.groovy
include("demo/shop/Ingredients.groovy")
 
fixture {
    margeritha(demo.shop.Pizza) {
        name = "Margherita"
        description = "Der Klassiker"
        price = 4.00
        ingredients = [ref(tomatoes), ref(mozzarella)]
    }
}

Der Name der mit ref angesprochenen Beans ist gerade derjenige, den wir in Ingredients.groovy verwendet haben. Was das include betrifft, ist hier zu beachten, stets den absoluten Pfad zu verwenden und die Dateiendung wegzulassen.

Nun sind wir schon fast fertig und müssen nur noch die BootStrap.groovy verkürzen. Hierfür reicht eine einzige Zeile:

fixtureLoader.load("demo/shop/Pizzas")

 

Anschließend stehen die soeben erstellten Objekte der Anwendung zur Verfügung und werden in der Datenbank persistiert. In Tests könnten wir beispielsweise die Pizza Margherita sehr einfach auffinden über Pizza.findByName("Margherita").

Wer sich selbst von den persistierten Daten ein Bild machen möchte, kann die hilfreiche dbconsole verwenden, die ebenfalls mit Grails ausgeliefert wird.

Eine sinnvolle und notwendige Erweiterung für einen Webshop besteht wohl darin, Fixtures für Benutzer des Webshops mit unterschiedlichen Rollen anzulegen, um so anhand von Tests die Zugriffsrechte auf Bereiche eines Webshops zu überprüfen. Nach dem oben beschriebenen Schema ist dies sehr einfach zu realisieren. In Verbindung mit dem SpringSecurityService ist es somit ein einfaches, Benutzer anzulegen:

fixture {
    sampleCustomer(demo.shop.Customer) {
        email = "testcustomer (a) test.org"
        fistName = "Theo"
        lastName = "Tester"
        password = springSecurityService.enttPassword("test-password")
    }
    / / ... add more here
}

Da der FixtureLoader den Applikationskontext beibehält, stehen injizierte Services wie der hier benutzte SpringSecurityService ebenfalls zur Verfügung.

Wir hoffen, diese Hinweise helfen, die Entwicklung zu beschleunigen und Tests zu verbessern.