Blog

Hilfreiche Fehlermeldungen in Grails

Erik Pragt

Aktualisiert Oktober 23, 2025
4 Minuten

Zurzeit bin ich dabei, eine Grails-Anwendung zu erstellen. Ich habe zwar schon mehrere Prototypen/schnelle Hacks erstellt, aber dies ist die erste 'echte' Anwendung, die ich baue. "Also", dachte ich, "wenn dies eine echte Anwendung ist, brauche ich auch echte Tests!". Wenn man sich im normalen Fluss der Entwicklung einer Grails-Anwendung befindet, geht alles so schnell, dass man fast vergisst, die Tests zu schreiben. Also beschloss ich, es ein bisschen anders zu machen, und zwar genau wie in Java: TDD!

Das Problem

Ich habe derzeit ein kleines Domänenmodell erstellt. Eine der Klassen in der Domäne wird als Aufgabe bezeichnet und sieht in etwa so aus:

Klasse Task {
  String-Titel
  String Beschreibung
  static constraints = {
  Titel(leer: false)
  Beschreibung(leer: true)
  }
}

Wie Sie hier sehen können, habe ich eine kleine Klasse mit 2 Eigenschaften erstellt, von denen die Beschreibung optional ist. Jetzt würde ich gerne wissen, ob meine Validierung korrekt ist, also habe ich einen Testfall erstellt. In Grails gibt es einen Unterschied zwischen Integrationstests und Unit-Tests. Die Integrationstests starten den gesamten Grails-Container, injizieren Ihre Domänenklassen mit den richtigen GORM-Methoden usw. usw., während die Unit-Tests das nicht tun: Sie sind nur schnelle Funktionstests. Der Testcase sieht so aus:

void testCreateTask() {
  assertNotNull new Task(description: 'Dies ist eine Aufgabe').save()
}

Wenn ich diesen Test ausführe ('grails run-app'), erhalte ich die folgende Ausgabe:

1 Integrationstest läuft... Test com.xebia.domain.TaskTest läuft... --Ausgabe von testCreateProject-- testCreateTask...FAILURE Integrationstests abgeschlossen in 719ms

Hmm, mein Test schlägt fehl. Aber warum? Ach so, ich habe vergessen, meiner Aufgabe einen Titel zu geben. Aber wo ist meine Fehlermeldung? Die Grails-Validierung weiß, was schief gelaufen ist, aber warum sehe ich im Protokoll nur einen FAILURE? Dabei erstellt Grails einen HTML-Bericht, der die Tests und ihre jeweilige Ausgabe enthält. Wenn Sie den Bericht öffnen, sehen Sie etwas wie das folgende Bild:

Grails HTML-Bericht

Leider gibt es keine Details darüber, warum der Test fehlschlägt! Die einzige Information, die der Stacktrace liefert, ist die Zeile, in der der Test fehlgeschlagen ist: 23, aber keine weiteren Informationen darüber, warum der Test fehlgeschlagen ist. N/A junit.framework.AssertionFailedError at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899) at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77) at com.xebia.domain.TaskTest.testCreateTask(GrailsProjectServiceTest.groovy:23) Wie ich bereits sagte, ist es in diesem Fall einfach: Fügen Sie einfach einen Titel hinzu, und der Test wird funktionieren. Aber wenn Sie ein komplexeres Verhalten verwenden, könnte es etwas schwieriger sein, herauszufinden, was schief gelaufen ist. Da ich in der Grails-Dokumentation keine richtige Lösung finden konnte, habe ich beschlossen, meine eigene Lösung zu erstellen.

Lösung

Um dieses Problem zu lösen, habe ich eine neue Methode mit dem Namen 'assertValid' definiert. Diese Methode (eigentlich eine Schließung) prüft, ob das übergebene Argument gültig ist, und wenn nicht, sammelt sie die mit dem Objekt verbundenen Fehlermeldungen und zeigt sie in einer assert-Anweisung an. Daraus ergibt sich der folgende Code:

GroovyTestCase.metaClass.assertValid  <
  println "assertValid"
  def validate = object.validate()
  def message = object.errors.allErrors.inject('') {str, error ->
  str + "Feldfehler in Objekt ${error.objectName} auf Feld ${error.field}: zurückgewiesener Wert [${error.rejectedValue}];"
  }
  assertTrue Nachricht, validieren
  assertNotNull object.save()
}

Ich habe hier die ExandoMetaClass-Funktion von Groovy verwendet, um dynamisch eine Methode zu GroovyTestCase hinzuzufügen, der Klasse, von der alle Tests abgeleitet sind. Die Methode assertValid wird hinzugefügt und steht nun in allen Instanzen von GroovyTestCase zur Verfügung. Das bedeutet, dass ich jetzt Folgendes tun kann:

void testCreateTask() {
  Aufgabe task = new Task(Beschreibung: 'Dies ist eine Aufgabe')
  assertValid Aufgabe
}

Die Ausführung dieses Vorgangs führt natürlich immer noch zu einem Fehler, aber anstelle der Meldung 'N/A' zeigt der Testbericht nun Folgendes an: Field error in object com.xebia.domain.Task on field title: rejected value [null];

Fazit

Durch die Nutzung der Spring-Fähigkeiten von Grails und der Groovy MetaClass-Funktionalität war es also recht einfach, detaillierte Informationen über die Fehler in der Grails-Domain-Bindung zu erhalten, und das auf eine sehr wiederverwendbare Weise! Ich hoffe, Sie haben diesen Blog als nützlich empfunden, und wenn Sie Kommentare haben, zögern Sie nicht und lassen Sie es mich wissen!

Verfasst von

Erik Pragt

Contact

Let’s discuss how we can support your journey.