Testdaten (DB erstellen, Daten füllen): Brauche eure Meinungen

slowfly

Erfahrenes Mitglied
Hallo zusammen

Vorwort:
Wir wollen uns verbessern. Verbessern insofern, dass wir eine anständige Testinfrastuktur für die Entwickler bereitstellen können. Dazu wollen wir ein einheitliches Vorgehen definieren, wie wir Testdatenbanken für Unit- und Regressionstests automatisiert aufsetzen und die entsprechenden Tests durchführen. Unter Unittests verstehen wir die JUnit-Tests, welcher der Entwickler implementiert. Unter Regressionstests verstehen wir, dass bei einem Check-In, diese JUnit-Tests durchgeführt werden, Stichwort Continious Integration.

Folgende Fragen habe ich:
1. Datenbank für Tests: Wie erstellen?
Unabhängig vom DBMS sehe ich hier als einfachste Lösung, dass wir die Struktur des Entwicklungsschema (oder -datenbank, je nachdem ob ORA oder MSSQL, wir haben beides) kopieren, inkl. Trigger, Sequences, etc, exkl. Daten. Dazu würden wir vermutlich die entsprechenden Tools des DBMS' verwenden.
Hier sehe ich das Problem, dass während Entwicklung und Änderungen am Schema die Regressionstests nicht durchlaufen können.
Ich sehe hier sonst keine einfache Möglichkeit, die Datenbank zur Verfügung zu stellen. Habt ihr noch Ideen? Was haltet ihr von diesem Vorgehen?

2. Testdaten: Wie füllen? (setup/teardown)
Mir fallen da zwei Möglichkeiten ein, welche ich selber schon mal im gleichen Zusammenhang verwendet habe:
2.1. SQL-Statements selber schreiben
Ziemlich simpel: setup.sql und teardown.sql. Händisch im JUnit-Test einlesen und ausführen.

2.2. Programmatisch mit Hibernate
Als Code-Beispiel
Code:
class Test{
  private Entity entity;
  void setup(){
    entity.setId(1l);
    save(entity)
  }

  void teardown(){
    delete(entity)
  }
}

Was haltet ihr von den beiden Vorgehensweisen? Fallen euch noch andere ein?
Mir persönlich hat die erstere Variante am besten zugesagt, weil es sehr einfach und schlank ist.
Was mir aber an beiden Varianten nicht gefällt, dass es nicht leserlich ist. Wenn spalten hinzugefügt werden, wenn man Werte vergleichen (assert) muss oder wenn man neue Datensätze aufnehmen muss, macht das nicht sonderlich Spass (und Arbeiten soll ja Spass machen ;). Aber ich habe irgendwie das Gefühl, dass es keine einfachere und bessere Variante gibt.

Habt ihr sonst noch Tipps zu bestimmten Vorgehen oder kennt ihr Tools zur Unterstützung?

Besten Dank fürs Lesen =)

Gruss
slowy
 
Ich kenne Firmen und Projekte da wird es mit Maven und z.B. Profile respektive Plugin gemacht. Das funktioniert eigentlich sehr gut.

z.B. mvn clean install -Poracle,testdb
dann wird gebaut und mittels Plugin die Scripte etc. fürs Füllen der DB genutzt. Man könnte hier dann auch angeben ob ich Test oder Produktionsdaten haben möchte. (z.B. proddb würde Produktionsdaten die deutlich größer sind populieren - etwa für Lasttests usw.)

Prinzipiell sollte dann über diesen Mechanismus eigentlich immer die DB gefüllt werden. Also auch die Entwickler sollten das nutzen, damit es nicht mehrere Möglichkeiten gibt und man immer sicher sein kann, dass das Schema ok ist und die DB gefüllt werden kann.
Für die Testdaten könnte man z.B. DBUnit nutzen.
Muss aber nicht sein.

Selbstverständlich musst du Datenbankabhängige Schemaänderungen oder Trigger u.ä. spezielle für die DB pflegen. Das zieht dann beim bauen mit an.

Wichtig ist auch: Migrationsscripte für die Schemaänderungen.
 
Hallo socke

Danke für deine Antwort. Mist, das habe ich vergessen zu erwähnen: Ja, wir verwenden maven. Test/Build/Release wird dann mit Maven durchgeführt.

Gruss
slowy
 
Also erstmal sehe ich hier keine Unit-Tests. Ein Test, der eine Datenbank benötigt, kann schonmal kein Unit-Test sein. Hier müsste man die DB-Schicht wegmocken und wirklich nur die Unit testen. Vielleicht wäre das schon eine Lösung.

Andererseits spricht natürlich nichts dagegen, auch Integrationstests mit DB zu haben. Ich mache das mit einer lokalen Java-Datenbank (H2), die direkt in der VM läuft. Das Schema kann direkt automatisch über Hibernate erzeugt werden. Bleibt das Problem der Testdatenerzeugung. Hier kommt es drauf an, wie umfangreich die Sache ist. Entweder man macht es im Testcode selbst oder verwendet SQL-Skripte (vielleicht aus DB-Dumps). Das muss man im Einzelfall sehen.
 
Salve

Danke schon mal für deine Antwort.

Meiner Meinung nach ist ein Unittest, der Operationen auf einer Datenbank impliziert, kein Oxymoron - wenn man jetzt mal die Definition aussen vor lässt ;). Unsere Applikationen gehen meist mit einer eigenen Datenbank Hand-in-Hand und führen "im Kern der Sache" sehr viele Datenbankoperationen aus, so dass wir eine Unit auch mit dem Datenbankzugriff identifizieren.
Und durch die vielen Datenbankoperationen würde ein "wegmocken" zu ziemlich viel Aufwand führen, welchen wir vermutlich nicht rechtfertigen könnten. Ich sehe Mocks eher bei Umsystemene wie Webservices oder Mailservices, oder Systeme, welche nur Test- und Produktionsprofil anbieten.

In-Memory-Datenbanken zu verwenden, ist jetzt sicher mal vom Bauchgefühl her nicht das, was wir brauchen/wollen. Wenn ich z.B. an sequences oder triggers denke, scheint mir das zu weit von der Realität entfernt zu sein. Oder Timestamps, Zeitzonen und BLOBs. Vielleicht bin ich da einfach etwas voreingenommen, aber ich bin schon der Meinung, dass das sich da irgend ein DBMS nicht so verhalten sollte, wie jenes, welches dann mit der Applikation zum Zug kommt. Und, dass die entsprechenden Testresultate nicht aussagekräftig sind.

Gruss
slowy
 
tfa hat schon recht, sofern die DB mitspielt sind es keine Unittests.
Oft wird sich gestritten ob ein Test der DAO-Klasse nun ein Unittest ist oder nicht, wenn er die DB aufruft. Aber strenggenommen schon, denn du testest dann icht mehr die einzelne Unit (DAO-Klasse) sondern gleich auch das Verhalten der DB. Das hat auch nichts damit zu tun ob die DB Hand-in-Hand mit dem Code geht und ihr vllt wenig Logik einsetzt. Der Punkt ist, du testest in dem Moment eine Black Box. Was wenn die DB nicht erreichbar ist? Oder in einem inkonsistenten Zustand? Im übrigen müssen Unit-Tests per se schnell sein, denn sonst führ diese keine aus. Gehts du über mehrere Komponenten oder DB, hast du evtl. ein Problem, da eben mehree Kompoenten am Test beteiligt sind. Kannst du das Verhalten aller Komponenten unter Test kontrollieren? Nein, und daher ist es dann ein Integrationstest, denn er testet die Integration mehrere Komponenten oder Units. Du wirst dann auch nicht alle Pfade testen können, denn das können analog zur Anzahl der involvierten Komponeten sehr viele werden. Aber das führt jetzt zu weit :)

Es steht nun jedem frei, seine Testscenarien zu definieren wie man möchte, allerdings aus Erfahrung kann ich nur von einer hohen Anzahl Integration-Tests abraten. Am besten alles Unittesten und für Integrationstests business-kritische Szenarien auswählen. Aber das sollte euer Architekt definieren :)

Übrigens, die Datenbankzugriffe wegmocken ist überhaupt kein Aufwand wenn du Interfaces für die DAOs hast. Dann erstellt du dir Fakes oder Stubs.
 
socke77 hat alles schon gesagt. Vielleicht noch eine Anmerkung:
Übrigens, die Datenbankzugriffe wegmocken ist überhaupt kein Aufwand wenn du Interfaces für die DAOs hast. Dann erstellt du dir Fakes oder Stubs.
Wenn man als Mocking-Framework JMockit einsetzt, braucht man nichtmal mehr Interfaces. Damit ist wirklich alles mockbar. JMockit ist sehr mächtig, teilweise etwas unheimlich (und dadurch everntuell nicht ganz ungefährlich), aber auf jeden Fall einen Blick wert.
 
JMockit, Mockito und Konsorten sind seeeehr praktisch. Einen Haken hat die ganze Sache, gerade beim mocken statischer Methoden: Laufzeit! Mockito z.B. lässt so eine Testklasse teilweise bis 200ms dauern. Bei 10 Testklassen sind das 2 Sekunden, bei 100 - 20s. Es kann also recht schnell dazu führen, dass die Unittests sehr lange dauern.
Es lohnt sich also immer sich mit dem manuellem Mocken, Stubben, Faken zu beschäftigen.
 
Hallo Jungs

Beste Dank für euren Input. Ich glaube, bevor ich bei mir nicht nochmal über die Bücher gegangen bin ("danke", grmpf :p), macht eine weitere Diskussion keinen Sinn.

Guten Start in die Woche
slowy
 
Zurück