Sind HSQLDB-Unit-Tests ein Anti-Pattern?

9

HSQLDB ist großartig. Es verfügt (auch) über einen eingebetteten Modus (kein dedizierter Server erforderlich), der ein schnelles Prototyping von Dingen wie Proof of Concepts ermöglicht, und eignet sich auch hervorragend für produktionsbereite Anwendungen als schnelle und einfache Speicherung verschiedener Daten.

Mindestens 90% der Projekte, an denen ich in den letzten Jahren gearbeitet habe, werden unvermeidlich Unit-Tests mit einer eingebetteten HSQLDB durchführen, wenn sie sich in irgendeiner Weise mit einer SQL-Datenbank befassen.

Sicher genug, diese Projekte (die meistens die Standard-Maven-Struktur verwenden) haben eine Menge "Unit-Tests" (oder zumindest eine Art von Tests, die sich jedoch im Bereich "Unit-Test" befinden des Projekts (wie "src / test / java")), die eine oder mehrere eingebettete Instanzen von HSQLDB verwenden, um einige CRUD-Operationen durchzuführen und die Ergebnisse zu überprüfen.

Meine Frage ist: Ist das ein Anti-Muster? Ist es aufgrund der Tatsache, dass HSQLDB einfach zu integrieren und der eingebettete Server sehr leicht ist, so, dass es als "Modell" einer tatsächlichen Datenbank übersehen werden kann, wenn dies nicht der Fall sein sollte? Sollten solche Tests nicht eher wie Integrationstests behandelt werden (da jeder von ihnen aus "etwas" UND der Integration von "etwas" in einen Datenbankserver besteht)? Oder fehlt mir etwas in der "Unit Test" -Definition, und wir können tatsächlich sagen, dass die Verwendung von HSQLDB wie dieser einfach dem "Verspotten der Abhängigkeiten und Testen nur des Unit" -Teils der "Unit Test" -Definition entspricht?

Shivan Drache
quelle
3
Wird diese Verspottung nicht nur auf die Datenbankebene angewendet? Ich würde nicht sagen, dass es ein Anti-Pattern ist, solange es eine einfache und angemessene Möglichkeit ist, die Module, die davon abhängen, einem Unit-Test zu unterziehen.
Kilian Foth
2
@KilianFoth ja, aber ist es immer noch spöttisch, wenn der "Mock" (die Datenbank) tatsächlich eine aktive Rolle im Testprozess spielt? Wenn Sie beispielsweise eine Klasse mit einer gewissen Abhängigkeit haben (obligatorisch für die Instanziierung der Klasse), wenn Sie diese Klasse einem Komponententest unterziehen möchten, verspotten Sie diese Abhängigkeit, aber dann testen Sie einfach die Methoden der Klasse, nicht
Kümmere dich
1
@gnat gut, ich persönlich sehe diese andere Frage eher als "wie man Unit-Test-Sachen auf der oben genannten Datenzugriffsschicht - sollten wir uns über die Datenzugriffsschicht lustig machen oder was", während meine Frage ist, wie man Unit-Tests ausreicht Ist HSQLD und dergleichen auf der Datenzugriffsebene selbst eine gültige Methode, um Dinge zu verspotten? "...
Shivan Dragon

Antworten:

8

Ich betrachte die Verwendung von HSQLDB (oder DBUnit) beim Testen meiner DAOs als einen notwendigen Schritt, um die Qualität des Codes zu gewährleisten, den ich beim Berühren einer Datenschicht schreibe. Wie bereits erwähnt, handelt es sich nicht um eine kugelsichere Lösung, sondern in Kombination mit ihren Dialekten kann sie einen angemessenen Teil Ihrer Fälle abdecken.

Mein einziger Punkt wäre: Seien wir vorsichtig, wenn wir über Unit- Tests sprechen . Wenn ich eine Klasse teste, die mit einer externen Komponente interagiert , die sich meiner Kontrolle entzieht (dh nicht deterministisches Verhalten erzeugen kann), betrachte ich sie automatisch als Integrationstest.

Angenommen, Sie haben die Logik herausgerechnet, in der Sie direkt mit Ihrem Datenspeicher (über JDBC-Treiber, ORM usw.) in einer konkreten Klasse interagieren, die von einer Schnittstelle verdeckt wird, und Sie würden einen geeigneten Komponententest für Ihr DAO entwickeln, indem Sie ihn übergeben Eine konkrete verspottete Implementierung der Schnittstelle, die eine Reihe bekannter Werte zurückgibt, anhand derer Sie sie testen werden.

Kappolo
quelle
5

IMO:

  • Wenn für einen Test eine Datenbank erforderlich ist, handelt es sich nicht um den Komponententest. Es ist entweder ein Integrations- oder ein Abnahmetest.
  • Um die Notwendigkeit der Verwendung von DB in einem Test zu vermeiden, sollte ich die Umkehrung des Steuerprinzips mithilfe der Abhängigkeitsinjektion (oder des Service Locator) verfolgen .
  • Wenn ich etwas mit DB testen muss, sollte ich eine VM mit tatsächlicher Datenbank erstellen (mit gefälschten oder realen Daten, abhängig von der Aufgabe). Vagrant und Docker sind großartige Werkzeuge für diesen Job.
  • Das Verspotten der DB ist eine schlechte Praxis. Es ist dasselbe, als würde man es falsch machen (DB verspotten) und dann richtig wiederholen (damit es mit einer echten DB funktioniert).
  • Es ist in Ordnung für Proof-of-Concept-Projekte, aber es folgt eine Wiederholung (wenn das Konzept bewiesen ist).
  • Es ist vollkommen in Ordnung, HSQLDB (oder was auch immer) alleine zu verwenden, wenn es ein geeignetes Werkzeug für den Job ist.
scriptin
quelle
4

Verwenden Sie für (Unit-) Tests dieselbe Datenbank, die Sie in Ihrer Produktionsumgebung verwenden.

Der Grund ist einfach: Datenbanken verhalten sich anders. Jede Datenbank hat ihre eigenen proprietären Funktionen. Selbst wenn Sie eine ORM-Schicht für den Datenbankzugriff verwenden, kann sich Ihre Datenbank auch in Bezug auf die Leistung unterschiedlich verhalten.

Der Vorteil einer eingebetteten Datenbank für Komponententests besteht natürlich darin, dass Sie nichts konfigurieren müssen. Es ist jedoch nicht so schwierig, eine Datenbank auf jedem Entwicklercomputer zu installieren und für die Verwendung für Komponententests zu konfigurieren.

Ich hatte vor einiger Zeit die gleiche Situation in einem Projekt. Wir haben JDBC verwendet, um SQL-Anweisungen in der Datenbank auszuführen. Als wir beschlossen, Tests für unsere Anwendung zu schreiben, verwendeten wir eine eingebettete Datenbank. Dies führte zu Situationen, in denen einige Fehler nicht mit Komponententests reproduziert werden konnten und einige Teile der Anwendung nicht getestet werden konnten, da die eingebettete Datenbank die Funktionen unserer Produktionsdatenbank nicht unterstützte. Daher hat jeder Entwickler dieselbe Datenbank installiert, die wir auch in der Produktionsumgebung auf dem PC verwendet haben, um die Tests auszuführen, was viel besser funktioniert hat.

Uooo
quelle
Das sind viele Oracle-Instanzen, die Sie in einigen Umgebungen installieren möchten - überhaupt nicht billig. Vergessen Sie auch nicht den Build-Server.
@ MichaelT Ich weiß, aber was ist die Alternative?
Uooo
2
Man könnte HSQLDB verwenden, um mit einem Orakeldialekt zu arbeiten (siehe sql.syntax_orain hsqldb.org/doc/guide/dbproperties-chapt.html ) - es ist nicht perfekt, aber für die meisten Dinge gut genug. Wie Sie bemerken, ist es nicht alles, aber es löst die Probleme mehrerer erforderlicher Lizenzen und ermöglicht eigenständige Builds, die nicht von einem anderen ausgeführten Dienst abhängen. Ein anderer Ansatz wäre dbunit . Es gibt tatsächlich Kompromisse bei jedem Ansatz.
@MichaelT Oracle kann kostenlos entwickelt / getestet werden. Die Installation einer anderen Oracle-Instanz zum Testen ist also kostenlos. Die Installation von mehr Hardware für die Ausführung all dieser Instanzen ist natürlich mit Kosten verbunden.
Jwenting
2
Ich würde es nicht als
Hermann
4

Ich habe einige wirklich schlimme Probleme gefunden und behoben und meine Entwicklung durch Tests mit der HSQL-Datenbank erheblich beschleunigt. Häufig kann die niedrigste Stufe von Adapterklassen (dh Ihre DAOs) beim Komponententest nicht getestet werden, da es schwierig sein kann, sie von einem tatsächlichen Roundtrip zu einer Live-Datenbank zu entkoppeln. Ich habe in der Vergangenheit Connection- und JDBC-Artefakte verspottet, aber es war tatsächlich viel schwieriger als das Testen gegen eine In-Memory-Datenbank.

In einem Projekt habe ich HSQL entwickelt und getestet, lokal gegen MySQL ausgeführt und für die Produktion gegen SQL-Server bereitgestellt. Erstaunlicherweise hat es gut funktioniert. Das größte Integrationsproblem, auf das ich gestoßen bin, war auf mein eigenes Missverständnis der Spezifikation zurückzuführen (Sekunden vs. Millisekunden, lol).

Während das Persistenz-Framework die zugrunde liegende Datenbank maskieren kann (wie es Hibernate tut), kann das Persistenz-Framework selbst genug komplizierte Semantik hinzufügen, so dass eine isolierte Ausübung viele Macken beseitigen kann, bevor Sie zu vollständigen Integrationstests gelangen.

Stellen Sie sich den Fall vor, in dem Sie Hibernate oder JPA verwenden, um eine komplizierte Ergebnismenge mit vielen Verknüpfungen zu erstellen. Wenn es böse ist und viele Versuche erfordert, können Sie dies anhand einer Live-Datenbank entwickeln und testen. Sie können es aber auch mit weniger Aufwand für eine speicherinterne Datenbank entwickeln und testen. Sie müssen sich auch nicht um Setup- / Teardown-Skripte für Ihre Datenbank-Fixtures kümmern oder Tests und Builds mit anderen koordinieren. Und wenn Sie mit der In-Memory-Datenbank testen, genießen Sie die Vorteile dieser Tests für die Regression, genau wie bei anderen Komponententests.

Ich habe den Begriff "Komponententest" für Tests gehört, die mehr als ein Komponententest sind, aber immer noch isoliert durchgeführt werden.

Unit-Tests, Komponententests (wenn das wirklich ihr Name ist) und Integrationstests bieten alle Wert. Teams können auf das eine oder andere verzichten (ja, viele Teams führen keine Komponententests durch und verlassen sich ausschließlich auf Integrationstests), aber wenn Sie dies tun, verlieren Sie die Vorteile, die die anderen Teststufen bieten.

Bei Tests, die aufgrund der Integration in den Speicher isoliert sind, besteht der Vorteil in einer schnellen Validierung des Codes und einer schnellen Regression - die Vorteile isolierter Tests und die Notwendigkeit zusätzlicher Installationen, Lizenzen oder Hardware. Sie können damit mehr Codeebenen isoliert validieren, als wenn Sie nur Ihre DAOs getestet hätten, bis die Datenbank bereitgestellt wurde.

Es gibt also einen praktischen Wert. Es ist nicht erforderlich, aber ich war eher glücklich als weniger, als ich es getan habe.

rauben
quelle
3

Ich benutze solche Ansätze in der Vergangenheit, ich bin mir nicht sicher, ob dies ein "Anti-Muster" ist (einige Leute versuchen, seine persönlichen Gefühle oder Erfahrungen zu universellen Regeln zu erheben, ich bin es nicht), aber ich bevorzuge einen anderen Ansatz:

  • Für den Komponententest werden beim realen Komponententest keine externen Elemente wie eine In-Memory-Datenbank verwendet (im Vergleich zu einer HashMap oder einem Stub mit einer Stub-Bibliothek nicht besonders leicht). Wenn Sie DI und ein einfaches Muster wie Repository oder DAO für Ihren Datenzugriff verwenden, ist dies sehr einfach zu erreichen. Mein Ziel ist es, eine Menge Unit-Tests zu haben, die sehr schnell ausgeführt werden, und mit schnellem Denken denke ich in weniger als einer Minute an einen 3k-Test.

  • Für den Integrationstest bevorzuge ich die Verwendung der realen Datenspeichersoftware in diesem Test. Das Ziel dieses Tests ist es, die Integration zwischen meiner Software und dem realen Datenspeicher zu beweisen. Die Verwendung eines anderen Datenspeichers bricht hier die Absicht Prüfung. Ich versuche, so wenig Integrationstest wie möglich zu schreiben, um diese Integration zu demonstrieren, und normalerweise wird dieser Test außerhalb des normalen Builds ausgeführt (zum Beispiel nur in nächtlichen Builds).

Ich benutze HSQLDB manchmal, um einfache Demos oder Proof-of-Konzepte zu erstellen. Es ist ein großartiges Tool, aber ich sehe nicht mehr, wo dieses Tool in meinem normalen Entwicklungsworkflow Platz findet.

AlfredoCasado
quelle
0

Für Unit-Testing-Datenzugriffsprodukte wie MyBatis sind Tests mit eingebetteter HSQLDB meiner Meinung nach vollkommen in Ordnung (und ich weiß zumindest bei MyBatis, dass sie HSQLDB genau dafür verwenden).

Für die meisten anderen Projekte würde ich diesen Overkill in Betracht ziehen. Ich würde einige einfache Integrationstests durchführen (dies sind Tests, die möglicherweise Nebenwirkungen auf andere Tests haben), die auf die reale Datenbank zugreifen und sicherstellen, dass jede Abfrage / Anweisung mindestens einmal verwendet wird. Das Projekt sollte weit weniger Integrationstests als Unit-Tests enthalten.

Für die Unit-Tests würde ich das DAO verspotten oder was auch immer auf die DB zugreift (der 'Adapter').

ihr Mann
quelle