Tests, HSQL und Apache DBCP
Wenn Software-Projekte auf Datenbanken angewiesen sind, wird das Testen oft unangenehm. Sollen Tests auf eine externe Datenbak angewiesen sein, und soll deren Adresse im Test-Code verankert liegen ? Elegant ist das nicht, und mit Embedded- und In-Memory-Datenbanken bietet sich eine gute Alternative.Um einige unserer Projekte flexibler für Tests zu machen, sollte in der Test-Phase also eine solche Embedded-Datenbak eingesetzt werden. Die Wahl viel auf HSQL, und beim ersten Projekt – das Hibernate verwendet – funktionierte die Umstellung einwandfrei und denkbar einfach. Hier die entsprechenden Zeilen aus der Hibernate-Config:
<property name="hibernate.dialect"> org.hibernate.dialect.HSQLDialec </property> <property name="hibernate.connection.driver_class"> org.hsqldb.jdbcDriver </property> <property name="hibernate.connection.url"> jdbc:hsqldb:mem:in-memory-db </property> <property name="hibernate.connection.username"> sa </property> <property name="hibernate.connection.password"> </property> <property name="hibernate.hbm2ddl.auto"> create-drop </property>
Achtung: die Property hibernate.hbm2ddl.auto sorgt mit dem angegebenen wert dafür, dass das Schema bei Nicht-Existenz angelegt wird, und die Datenbak am Ende auch wieder gelöscht wird. Diese Property sollte also nur für den Entwicklungs- und test-Betrieb verwendet werden !
Mit der angegebenen Connection-URL wird eine In-Memory-Datenbak verwendet. Alternativ kann man mit HSQL auch eine Filesystem-basierte Datenbak erzeugen:
<property name="hibernate.connection.url"> jdbc:hsqldb:file:target/fsdb </property>
Werden die Builds z.B. mit Maven ausgeführt, so kann man mit der oben genannten, relativen Connection-URL dafür sorgen, dass eine File-basierte Datenbank im target-Verzeichnis (das Default-Build-Verzeichnis) landet. So lässt sich nach den Builds ggf. die Datenbak noch inspizieren, aber vor einem neuen Build kann mit dem clean-Lifecycle von Maven dafür gesorgt werden, dass das Verzeichnis, und damit auch die Datenbank gelöscht wird.
Nachdem das bei einem Projekt so einwandfrei funktionierte,sollte gleich das nächste folgen. Dort stiess ich allerdings auf unerwartete Probleme. Ich änderte die Datenbankeinstellungen für die Tests analog zum ersten Projekt, doch als die Tests liefen bekam ich folgende Fehlermeldung
... Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Could not create a validated object, cause: ValidateObject failed ...
Caused by: java.util.NoSuchElementException: Could not create a validated object, cause: ValidateObject failed
Wie aus dem Stacktrace ersichtlich ist, benutzt das Projekt Apache Commons DBCP, um die Datenbank-Connections zu poolen. Irgendwo dort schien das Problem zu liegen. Nach kurzer Suche stiess ich auf diesen Thread, der mich darauf brachte es mal mit der Validierungs-Query zu versuchen. Der Connection-Pool benutzt zur Validierung die Query “SELECT 1;”, die mit HSQL offenbar Probleme bereitet.
Ein schneller Workaround war, die Validierung der Connections bei den Tests abzuschalten, da dieses Feature ohnehin nur mit anderem Setup Sinn macht, nämlich im Produktiv-Betrieb unter Verwendung mit MySQL (ich sage nur Timeout-Probleme!). Nachdem am DBCP-Pool das Flag testOnBorrow auf false gesetzt war, liefen die Tests mit der HSQL-Datenbak einwandfrei durch.
Diese Umstellung bringt einige Vorteile:
- die Tests laufen in den meisten Fällen schneller, da die Performance der In-Memory-Datenbak meist deutlich besser ist, als einen (ggf. entfernten) Datenbank-Server zu verwenden
- die Projekte sind portabler, da sie nicht mehr von einem Datenbank-Server abhängig sind, der dann auch für alle beteiligten Entwickler erreichbar sein muss (auch nicht von einem evtl. vorhandenen Build-Server aus)
- sollten die Tests bisher auf einer Datenbak gelaufen sein, die evtl. auch im Betrieb (beispielsweise im Staging) eingesetzt wurde (wovon natürlich trotzdem abzuraten ist), so gibt es nun keine Gefahr mehr einen inkonsstenten Datenbestand durch unbedachte Änderungen zu erzeugen
- …
Alles in allem auf jeden Fall eine lohnenswerte Änderung, die die Arbeit an vielen Stellen leichter macht.