PHP UnitTests mit DB-Simulation

Testen ohne Datenbank ist zwar schon ganz nützlich, aber viele Funktionen basieren dann doch stark auf DB-Daten. Mocken wird oft sehr aufwendig wenn wir mit Frameworks arbeiten, die die Models für uns automatisch laden. Und es soll ja auch Funktionen geben, die erst ab 100 Datensätzen richtig warm laufen. Das will natürlich niemand mit einem Mock abbilden.
In diesem Beitrag beschäftigen wir uns also mit der Simulation einer Datenbank.


Grundlegendes

Wir haben natürlich immer noch keine MySql-Datenbank-Anbindung für unser CI-System, aber wir haben vermutlich etwas freien Arbeitsspeicher. Die Lösung ist also eine SQLite DB im Arbeitsspeicher. Dazu muss möglicherweise ein PHP-Paket auf dem Server installiert werden, aber das sollte jeder hinbekommen.
Du verwendest mysql_query() in deinem Projekt? Dann bist du hier leider falsch und hast sowieso größere Probleme als UnitTests. Geh und bring das in Ordnung!

Datenbank Handling

Im letzten Artikel haben wir schon die Basisklasse für unsere Tests gesehen. Jetzt ist einiges an Funktionalität dazu gekommen:

tearDown() wird von phpUnit nach einem Test aufgerufen. Hier sorgen wir dafür dass die DB-Verbindung zerstört wird um für jeden Test eine saubere Umgebung zu haben. Wer möchte kann das auch zu Beginn der setUp() tun, ich finde aber es ist thematisch besser hier aufgehoben.

Wir halten $_db private um unnötigen Overhead in Tests ohne Datenbank zu vermeiden. So müssen wir die DB-Verbindung erst aufbauen, wenn sie denn auch benötigt wird (Aufruf von getDb()).

Schemas

In einer separaten Datei halten wir alle unseren Tabellen Schemas vor. Diese ändern sich nur selten und sind für alle Tests identisch. Hier müssen wir keine komplizierten Strukturen anlegen, ein einfaches PHP-Array reicht völlig.

Tabellenname als Key und das Create-Statement als Value. Zu beachten sind lediglich, dass wir MySql-spezifische Befehle entfernen. Dazu zählen vor allem AUTO_INCREMENT, ENGINE und CHARSET.


Test mit Db

Unser Test sieht dann folgendermaßen aus:

Zunächst definieren wir welche Tabellen wir verwenden möchten mit „$this->tables“. Da wir für jede Tabelle einen Drop und ein Create ausführen, sollten wir uns auf die benötigten Tabellen beschränken. Auch ein CI-System hat nur begrenzte Ressourcen.
Wenn die ganze Klasse mit bestimmten Tabellen arbeitet, kann das auch in die setUp() verlegt werden.

Mit „$this->getDb()“ initiieren wir die Verbindung und lassen die Tabellen anlegen.
Danach muss dann auch noch irgendwas in die DB geschoben werden, wir haben an dieser Stelle nur leere Tabellen. Der Einfachheit halber hier im Beispiel nur ein Insert. Hier funktionieren aber auch alle Model-Logiken ala

Nur immer dran denken: Wir haben kein AUTO_INCREMENT. Ids müssen händisch vergeben werden.

Hier noch unser Testsubjekt:

Da der Test per Insert 123 Xp festlegt, erwarten wir die natürlich auch als Antwort.

Et voilà schon haben wir einen laufenden Test mit „richtiger“ DB-Anbindung.