Was wäre der beste Ansatz beim Unit-Testen eines Modells, das in eine Anwendung integriert ist, die eng an die Datenbank gekoppelt ist?
Das spezielle Szenario hier ist ein Einkaufswagen - ich möchte das Hinzufügen, Entfernen und Abrufen von Artikeln aus dem Einkaufswagen sowie die Preislogik usw. testen können. Dies alles erfordert in meinem Kopf Datenbankzugriff, obwohl ich das mehrmals gelesen habe Datenbankzugriff sollte vermieden werden.
unit-testing
user1189880
quelle
quelle
Antworten:
Die Abhängigkeitsinjektion ist eine Möglichkeit, dies zu handhaben. Sie können eine Testdatenbank einrichten, um den Einkaufswagen nachzuahmen, oder Sie können sogar einen Code schreiben, der die Transaktion des Kunden "bestätigt". Anschließend wählt Ihre Software zur Laufzeit die Komponente aus, zu der eine Verbindung hergestellt werden soll.
Stellen Sie während des Testens einfach keine Verbindung zur Produktionsdatenbank her!
quelle
Im Komponententest müssen Sie die Grenze dessen definieren, was Sie testen. Unit-Tests unterscheiden sich von Integrationstests. Wenn die Preislogik unabhängig vom Inhalt des Warenkorbs ist, testen Sie dies separat. Wenn dies nicht der Fall ist und alle Module eng miteinander verbunden sind, erstellen Sie eine Testumgebung, die die Produktion so gut wie möglich nachahmt, und arbeiten Sie damit. Ich glaube nicht, dass Abkürzungen und Simulationen langfristig helfen.
quelle
Das Modell sollte nicht von einer (konkreten) DB abhängen. Wenn es nur einen abstrakten DB (read "interface") kennt, der an das Modell übergeben wird, können Sie den DB durch ein Scheinobjekt ersetzen .
quelle
Ich hatte ein ähnliches Problem - ich hatte keine Möglichkeit zu garantieren, dass meine Test-DB die Werte beibehält. So bekomme ich in Zukunft zB andere Preise.
Ich extrahierte die Daten , die ich in eine kleine benötigt SQLite - DB und DB verwendet , um diese für meine Tests. Die Test-DB ist nun Teil des Setups meines Unit-Tests.
quelle
"Best" ist subjektiv, aber Sie könnten einfach eine Test-DB-Verbindung verwenden.
Laden Sie mit Fixtures einige Testdaten (Beispielprodukte zum Kaufen) und schreiben Sie dann den Testfall für die Klasse / Funktion, die Sie testen möchten.
quelle
Ich habe ein Plugin für Symfony 1.4 (PHP) erstellt , um dieses Problem zu lösen (unter anderem). Es orientiert sich an der Funktionsweise von Djangos Test-Framework (Python) : Das Framework erstellt und füllt eine separate Testdatenbank vor jedem Teststart und zerstört die Testdatenbank nach Abschluss jedes Tests.
Ich hatte ein paar Bedenken hinsichtlich dieser Strategie, sowohl hinsichtlich der Leistung (wenn sich das Schema nicht ändert, warum nicht einfach die Daten löschen, anstatt die gesamte Struktur neu zu erstellen?) Als auch hinsichtlich der Benutzerfreundlichkeit (manchmal möchte ich die Datenbank nach einer Überprüfung überprüfen) Test fehlgeschlagen, also nicht wahllos zerstören!), also habe ich einen etwas anderen Ansatz gewählt.
Vor den ersten Testläufen wird die Datenbank zerstört und neu erstellt, falls seit dem letzten Test Modelländerungen aufgetreten sind. Vor jedem weiteren Testlauf werden die Daten in der Datenbank gelöscht, aber die Struktur wird nicht neu erstellt (obwohl bei Bedarf eine manuelle Neuerstellung durch einen Test ausgelöst werden kann).
Durch selektives Laden von Daten-Fixtures in jeden Test kann die richtige Umgebung für diesen Test erstellt werden, ohne nachfolgende Tests zu beeinträchtigen. Fixture-Dateien können auch wiederverwendet werden, was diese Aufgabe viel weniger lästig macht (obwohl es immer noch mein am wenigsten favorisierter Teil beim Schreiben von Tests ist!).
In beiden Test-Frameworks ist der Datenbankadapter so konfiguriert, dass die Testverbindung anstelle der "Produktions" -Verbindung verwendet wird, um zu verhindern, dass die Testausführung vorhandene Daten beschädigt.
quelle
Ich würde sagen, gehen Sie einfach voran und verwenden Sie Fixtures, um die Daten vorab zu laden. So scheinen Unit-Test-Frameworks im Allgemeinen zu funktionieren, wenn Sie die Manipulation von Daten testen.
Aber wenn Sie wirklich vermeiden möchten, eine Verbindung zu einer Datenbank jeglicher Art herstellen zu müssen, und die allzu strenge Definition befolgen möchten, dass Komponententests nichts außerhalb des Codes berühren, sollten Sie sich die Objektverspottung ansehen - sie kann Ihnen Ideen geben.
Anstatt den SQL-Code direkt an der Stelle abzulegen, an der Sie ihn benötigen, können Sie beispielsweise eine Methode aufrufen, die nur die Aufgaben dieses SQL-Codes ausführt. Verwenden Sie
Person.getPhoneNumber()
zum Beispiel anstelle vonSELECT phone_number FROM person WHERE id = <foo>
. Es ist nicht nur auf einen Blick übersichtlicher und verständlicher, sondern Sie können während des Testens das Person-Objekt verspotten, sodassgetPhoneNumber()
es immer zurückkehrt555-555-5555
oder so, anstatt die Datenbank zu berühren.quelle
Dies ist ziemlich einfach mit junit zu tun, wenn ein wenig langatmig.
Das "Setup" sollte einen Satz temporärer Tabellen definieren und füllen.
Anschließend können Sie die Komponententests für alle Funktionen zum Aktualisieren, Einfügen und Löschen durchführen.
Rufen Sie für jeden Test, den Sie aufrufen, Ihre Aktualisierungsmethode auf, und führen Sie dann SQL aus, um das erwartete Ergebnis zu überprüfen.
In der "Teardown" -Phase lassen Sie alle Tabellen fallen.
Auf diese Weise führen Sie immer dieselben Tests mit denselben Anfangsdaten durch. Wenn Sie die Tabellen zwischen den Tests beibehalten, werden sie durch fehlgeschlagene Tests "verunreinigt". Außerdem ist ein konsistenter "Einfügetest" fast unmöglich, da Sie bei jedem Test immer wieder neue Schlüssel erfinden müssen.
quelle