Wie kann ich Unit-Tests und TDD verwenden, um eine App zu testen, die hauptsächlich auf Datenbank-CRUD-Operationen beruht?

22

Bei der Arbeit geht es in einem meiner Projekte hauptsächlich darum, Daten, die von einem externen Kunden übermittelt wurden, in einer Datenbank zu speichern. Es ist eine Java-Unternehmensanwendung, die JPA verwendet, und der größte Teil unserer Logik dreht sich um CRUD-Operationen.

Die meisten unserer Fehler betreffen JPA auf die eine oder andere Weise.

  • Beispiel 1: Wenn Sie zweimal auf die Schaltfläche Speichern klicken, versucht JPA möglicherweise, dieselbe Entität ein zweites Mal in die Datenbank einzufügen, was zu einer Verletzung des Primärschlüssels führt.
  • Beispiel 2: Sie rufen eine Entität aus der Datenbank ab, bearbeiten sie und versuchen, ihre Daten zu aktualisieren. JPA versucht möglicherweise, eine neue Instanz zu erstellen, anstatt die alte zu aktualisieren.

Oft muss die Lösung eine JPA-Anmerkung hinzufügen / entfernen / ändern. Ein anderes Mal hat es mit dem Ändern der DAO-Logik zu tun.

Ich kann nicht herausfinden, wie ich mit Unit-Tests und TDD Vertrauen in unseren Code gewinnen kann. Ich bin mir nicht sicher, ob es daran liegt, dass Unit-Tests und TDD schlecht zusammenpassen, oder ob ich mich dem Problem falsch nähere.

Unit-Tests scheinen nicht zu passen, da ich diese Probleme nur zur Laufzeit feststellen kann und sie auf einem App-Server bereitstellen muss, um die Probleme zu reproduzieren. In der Regel muss die Datenbank einbezogen werden, die meiner Meinung nach außerhalb der Definition eines Komponententests liegt: Hierbei handelt es sich um Integrationstests.

TDD scheint schlecht zu passen, da die Rückkopplungsschleife von deploy + test so langsam ist, dass ich sehr unproduktiv bin. Die Rückkopplungsschleife von deploy + test dauert mehr als 3 Minuten. Dies gilt nur, wenn ich die Tests speziell für den Code durchführe, den ich schreibe. Das Ausführen aller Integrationstests dauert mehr als 30 Minuten.

Es gibt Code außerhalb dieser Form und ich teste ihn immer, wann immer ich kann. Aber die Mehrheit unserer Bugs und die größten Zeitverluste betreffen immer JPA oder die Datenbank.


Es gibt eine andere Frage, die ähnlich ist , aber wenn ich den Rat befolgen würde, würde ich den instabilsten Teil meines Codes (den JPA) packen und alles außer ihm testen. Im Zusammenhang mit meiner Frage würde ich in der gleichen schlechten Situation sein. Was ist der nächste Schritt nach dem Einwickeln der JPA? IMO ist diese Frage (vielleicht) ein Schritt, um meine Frage zu beantworten, aber keine Antwort darauf.

Daniel Kaplan
quelle
4
Was Sie tun, ist im Wesentlichen Integrationstest, da Sie die Datenbank einrichten müssen, um tatsächlich zu testen. Ich kann mir vorstellen, dass ein Modul auf andere Module angewiesen ist, also ist es eher ein Integrationstest. Ich würde die Frage ändern, wie Sie TDD-Ansätze auf Ihre Anwendung anwenden können.
InformedA
@randomA richtig, ich habe meine Frage bearbeitet, um das ausdrücklich zu sagen. Ich verstehe nicht, warum Sie mir empfehlen, die Frage zu ändern. Können Sie näher darauf eingehen? Ich möchte das Gerät Testteil dort halten , weil ich würde eher Unit - Tests zu schreiben, als Integrationstests (obwohl ich bewusst bin , dass unit testing != TDD)
Daniel Kaplan
aber nichts besonderes, lege TDD einfach dort ab. Wenn Sie dort Unit-Test haben, dann würden viele Leute denken, Sie verstehen etwas nicht, etc .. nicht gut für Sie ..
InformedA

Antworten:

7

Eine Möglichkeit besteht darin, eine speicherinterne Testdatenbank wie H2 zu verwenden . Es ist in der Regel etwa 10-mal schneller als eine standardmäßige festplattenverwendende Datenbank und weist geringere Start- / Herunterfahrzeiten auf.

Ob dies hilft, hängt weitgehend davon ab, ob die JPA-Probleme, die Sie haben, allgemein genug sind, dass sie in einer anderen Datenbank immer noch fehlschlagen. Es macht nicht viel Sinn, Tests schneller durchzuführen, wenn der Großteil der Probleme übersehen wird.

Aber wenn Sie 10 Läufe mit H2 für jeden mit dem vollen System machen können, könnte es sich auszahlen.

Soru
quelle
Das ist ein guter Gedanke, aber ich muss ihn immer noch auf dem App-Server AFAIK bereitstellen. Das ist ein Großteil der 3+ Minuten. Das heißt, es lohnt sich auf jeden Fall. Es ist jedoch immer noch schwer vorstellbar, die Tests so oft auszuführen, wie ich Unit-Tests ausführen würde, und daher scheint es ineffizient, sie mit TDD zu entwickeln.
Daniel Kaplan
1
Ich denke, es gibt normalerweise Möglichkeiten, diese Anforderung zu umgehen (z . B. docs.oracle.com/middleware/1212/toplink/TLADG/testingjpa.htm ). Ziemlich hohe Wahrscheinlichkeit, mehr zu arbeiten als gerechtfertigt zu sein; Eine andere Möglichkeit wäre, einige leistungsstärkere Testserver zu installieren und die Dinge parallel zu betreiben.
Soru
1
@tieTYT Mein eigener Proof of Concept mit dem hsqldb-Unit-Test einer rohen Web-App auf github: TestingWithHsqldb - für die Unit-Tests muss die App nicht bereitgestellt werden.
3

Datenbanken können sehr einfach zu Unit-Tests werden - Sie benötigen gespeicherte Prozeduren und Transaktionen.

Dies ist, was Microsoft über das Testen von Datenbankeinheiten sagt . Sie können auch Komponententests für eine Datenbank ausführen, Ihre Tests in Java oder C # schreiben, indem Sie eine DB-Verbindung einrichten, eine Transaktion starten, alle Daten, die Sie für den Test verwenden möchten, in die DB schreiben, die Tests ausführen und dann ein Rollback durchführen. Keine Beschädigung der Datenbank, wenn Sie eine verwenden, für die Sie auch bereitgestellt haben, und Sie erhalten vollständig isolierte Tests.

Hoffe, dies kann Ihnen einen Einblick geben, wie Sie dies in Ihrem Rahmen tun können.

gbjbaanb
quelle
Wie ich bereits sagte: "Die Mehrheit unserer Fehler betrifft JPA auf die eine oder andere Weise." Ich denke, der Rat des Artikels würde all diese vermissen. Wenn Sie diese Java / C # -Tests weiterhin als Komponententests betrachten, haben wir sehr unterschiedliche Definitionen. Ich denke, dies ist im Allgemeinen ein guter Rat, aber es scheint immer noch, als würde es eine Menge Zeit in Anspruch nehmen, die Suite bereitzustellen und auszuführen, und wäre daher für TDD nicht förderlich. Stimmst du nicht zu
Daniel Kaplan
Früher haben wir DB-Komponententests für unser SQL ausgeführt, aber dann befanden sich alle in Sprocs. Während Sie sql Unit - Test von anderen SQL - Prozeduren, unser Unit - Test - Framework MSTest waren , so dass es Sinn machte , sie von dort zu laufen (hey, wir bekommen grüne Häkchen in dem Build - Server, war der wichtigste Faktor). Wenn Sie immer eine Datenbank haben (und wir haben es trotzdem für int. Testing gemacht), ist es einfach, den gesamten SQL-Code hochzuladen und alle Komponententests auf dem Build-Server auszuführen. Manchmal muss man in diesen Dingen einfach pragmatisch sein.
Gbjbaanb
Ich glaube nicht, dass du auf meinen ersten Satz geantwortet hast.
Daniel Kaplan
dann benutze einfach jpa-unit. Ich kann nicht beantworten, wie Ihr JPA-Code funktioniert (oder nicht), versuchen Sie einfach, Ihnen einige Ideen zu geben, wie Sie diesen SQL-Code in der Datenbank testen lassen können.
Gbjbaanb
3

Andere Leute haben mit "Mock out your DB!" Geantwortet. - aber was nützt es, Ihre DB-Ebene zu verspotten, wenn Sie tatsächlich testen müssen, wie sie mit Ihrem Code interagiert?

Was Sie suchen, sind Integrationstests und / oder automatisierte UI-Tests. Sie haben erwähnt, dass das Problem auftritt, wenn:

*If you click the save button twice*

Die einzige Möglichkeit, dies zu testen, besteht darin, einen automatisierten UI-Test zu schreiben, bei dem Sie zweimal auf die Schaltfläche klicken. Vielleicht sollten Sie Selen ausprobieren.

Sie werden wahrscheinlich auch einen Unit-Test-DB benötigen und für Ihre Tests darauf hinweisen. Ein Schmerz, den es zu bewahren gilt, aber willkommen bei TDD in der realen Welt.

Rocklan
quelle
dies liest sich eher wie ein Schimpfen als wie eine Antwort
Mücke
Ich habe die Frage dreimal beantwortet - Integrationstests, GUI-Tests und / oder eine Unit-Testing-Datenbank. Ja, es ist ein bisschen scherzhaft, ich werde es jetzt zu einem gewissen Anschein von Vernunft bearbeiten.
Rocklan
1
"Die einzige Möglichkeit, dies zu testen, besteht darin, einen automatisierten UI-Test zu schreiben, um zweimal auf die Schaltfläche zu klicken. Vielleicht sollten Sie Selen ausprobieren." In solchen Situationen sollte das Backend das verhindern, da sonst die Benutzeroberfläche direkten Zugriff auf die Datenbank hat.
Daniel Kaplan
0

In dem Beispiel, das Sie in Ihrer Frage angegeben haben, können Sie sich mit Unit Test / TDD nicht in die Situation begeben, dass Sie zweimal auf die Schaltfläche klicken, um auf einfache Weise einen Fehler zu verursachen. Was Sie jedoch testen können, ist, dass der Code, der beim Klicken auf die Schaltfläche aufgerufen wird, eine Ausnahme von der Persistenzschicht darstellt, die Sie entsprechend behandeln (indem Sie die Persistenzschicht verspotten oder eine speicherinterne Datenbank als verwenden) wurde in anderen Antworten vorgeschlagen) - entweder durch erneutes Werfen oder Anzeigen eines Fehlers oder was auch immer.

Sie haben Recht, dass TDD zusammenbrechen kann, wenn Sie Tests durchführen müssen, die nicht für einen Komponententest geeignet sind (z. B. Integrations- / Systemtests) Tot?" Debatten zwischen Kent Beck, Martin Fowler und David Heinemeier Hansson: http://martinfowler.com/articles/is-tdd-dead/

Chris Cooper
quelle