Migration von Grails 1.3.7 nach 3.1.9 - Testing - Teil 3

von Sebastian Jäger

Dies ist der dritte Beitrag über die Migration einer Grails Applikation. Im ersten Teil habe ich mich mit allgemeinen Schwierigkeiten und Lösungen befasst und im zweiten Teil eine selbstentwickelte Lösung zum Erstellen von Testdaten vorgestellt. In diesem Beitrag werde ich speziell die Tests beleuchten.

Testarten

In der existierenden Applikation gibt es eine übersichtliche Anzahl von Unit-Tests. Der größte Teil der Integrationstests besteht aus Controller- Integrationstests und nur wenigen Service- Integrationstests. Als funktionale Tests wurden historisch bedingt die Frameworks Geb und Webtest eingesetzt.
In früheren Versionen konnte das Testing Framework Spock über ein Plugin eingebunden werden, während es in neueren Versionen direkt in Grails integriert ist. Auch Geb muss nicht mehr als Plugin eingebunden werden sondern wird direkt unterstützt. Webtest ist ein Plugin das zum automatischen Testen von Web-Applikationen implementiert wurde, jedoch nicht mehr weiter entwickelt wird und deshalb nicht mehr verwendet werden kann.

Unit Tests

Da schon in der ursprünglichen Version das Test Framework Spock verwendet wurde, konnten die Unit Tests ohne große Schwierigkeiten migriert werden. Hauptsächlich sind Funktionsaufrufe überflüssig, die durch Annotationen ersetzt wurden.

Integrationstests

Es wird empfohlen Controller möglichst als Unit Tests zu realisieren und Services mit Integrationstests zu testen. Sämtliche Versuche, die bestehenden Controller-Integrationstests zu migrieren, blieben erfolglos. Man kann ein Objekt eines Controllers erstellen, jedoch war es nicht möglich, diesen mit allen Abhängigkeiten zu initialisieren. Schwierigkeiten hierbei waren die Hibernate-Session und HTTP-Sessions, die nicht korrekt funktionierten, so dass bspw. ein Service keine Daten der Datenbank abfragen oder die HTTP- Session bearbeiten konnte.

Wir haben uns dazu entschlossen, den Empfehlungen zu folgen und die Tests zu ändern. Das bedeutet für die Controller neue Unit Tests zu schreiben und für die Services Integrationstests. Ich glaube, dass durch diesen Schritt eine höhere Robustheit gegenüber kommenden neueren Versionen geschaffen wird, da jetzt ausschließlich native Bordmittel von Grails verwendet werden.
 

Beim Migrieren der Service-Integrationstests macht sich vor allem Grails' Umstellung auf Spring- Boot bemerkbar, das seit der Version 3.0 verwendet wird [1]. Denn ab jetzt wird die setupSpec Funktion von Spock, der ersten Testklasse, vor dem Start der Applikation ausgeführt, was deren Nutzen stark einschränkt. Beispielsweise ist das Anlegen oder Laden von Daten aus der Datenbank, die zum Testen verwendet werden sollen, nicht mehr möglich, denn sämtliche Hibernate- Funktionen sind nicht verfügbar. Weiter wird der where Block, der in Spock verwendet werden kann, vor dem Ausführen der setup- Methode ausgewertet. Somit sollten dort statische Werte verwendet werden, wie Stringint oder boolean. Es ist möglich, eine ähnliche Funktionsweise wie die setupSpec Funktion nachzubilden.
Dazu wird eine statische boolean Variable initialized = false angelegt. In der setup-Funktion wird zusätzlich zu dem Code, der vor jeder Testmethode ausgeführt werden soll, eine if- Condition verwendet um Code zu kapseln, der nur ein einziges mal zu Beginn ausgewertet werden soll. Hierzu wird mit !initialized überprüft, ob dieser Teil bereits einmal durchlaufen wurde.  Am Ende der if- condition wird die Variable initialized auf true gesetzt und somit wird dieser Codeteil nicht noch einmal ausgeführt. Das nachfolgende Codebeispiel veranschaulicht das Prinzip.

class BeispielTest extends Specification {
  
  static boolean initialized = false
  
  def setup(){
    if(!initialized){
      // Code, der hier steht, verhält sich ähnlich der "setupSpec" Methode
      initialized = true
    }
    
    // Code, der hier steht, hat das normale Verhalten der "setup" Methode
  }
  
  // Tests...
}

Funktionale Tests

Da in der ursprünglichen Version ebenfalls schon Geb verwendet wurde, können diese Tests ohne größere Anstrengungen migriert werden. Auch hier sind nur vereinzelt kleinere Änderungen durchzuführen. Zum Beispiel müssen alle Testklassen mit der Annotation @Integration markiert werden, oder der contextPath muss bei der Url Definition der pages erweitert werden, da Grails diesen nicht mehr standardmäßig auf den Namen der Applikation setzt [2].

Das dem Grails Webtest Plugin zu Grunde liegende Projekt Canoo Webtest wird nicht mehr entwickelt und somit ist auch das Grails Plugin nicht mehr aktuell und funktionsfähig. [3] [4] Es gibt somit keine Alternative außer der Neu-Implementierung dieser Tests mit Geb. Ich habe eine - wenn auch sehr umständliche - Möglichkeit gefunden, die alten Webtests gegen die migrierte Applikation auszuführen. Der Vorteil hierbei ist, dass die neue Applikationen während der Migration auf die richtige Funktionalität geprüft werden kann. Somit hat man die Gewissheit, dass sie wie gewünscht funktioniert, auch wenn noch keine umfangreichen neu implementierten Tests vorhanden sind.
Dazu lässt sich in der Konfigurationsdatei webtest.properties mit den folgenden drei Zeilen einstellen, welcher Server getestet werden soll. Da dadurch die alte Applikation gestartet wird, muss darauf geachtet werden, dass sie mit einem anderen Port wie die Neue startet, da es sonst zu Fehlern kommt.
wt.config.host      = localhost
wt.config.port      = 9090
wt.config.protocol  = http

Fazit

Die Migration der Tests ist in unserem Fall der größte Teil Umstellung auf Grails 3.1.9. Da es in früheren Versionen noch mehrere Test-Möglichkeiten gab und auch Grails sich stark weiter entwickelt hat, ist es leider nicht immer möglich, alte Tests zu migrieren - ein großer Teil der Tests muss komplett neu geschrieben werden.
Ich hoffe ich kann mit dieser Blog-Artikel-Reihe über die Migration einer großen Grails Applikation einigen Interessierten einige Arbeit ersparen, indem sie auf die aufgezeigten Lösungen zurückgreifen.

Kategorien: GebGrailsGroovyTesting

Zurück