PHPUnit Datenbank-Interaktionen testen

In diesem Artikel zeige ich wie man mit PHPUnit die Datenbank-Interaktionen testet. Für das Tutorial verwende ich das PHPUnit Framework und dessen Datenbank Erweiterung.

Voraussetzung (Grundkenntnisse in):

  • PHP
  • MySQL
  • PHPUnit
  • TDD (Test Driven Developement)

##Einleitung Für dieses Tutorial habe ich zwei Klassen erstellt. Einmal die "Database" und die "Person" Klasse. Zusätzlich gibt es noch eine "config.php" in der sich die Daten für die Datenbank Verbindung befinden.

Ich werde nicht in detail auf diese Klassen eingehen aber soviel zum Ablauf:

###Database Klasse

Diese Klasse sorgt für die Datenbank Verbingung. Durch die connect() Methode kann ich auf die DB zugreifen. Beim connect() werden die Daten für die Verbindung aus der config.php geladen. Dadurch kann ich zwischen der "live" und "test" Datenbank wechseln.

###Person Klasse

Diese Klasse erzeugt die "person" Tabelle und stellt CRUD ( create, update, delete ) Methoden zur verfügung. Die Person Klasse inkludiert die Database.php.

Wenn ich die Person Klasse aufrufe, wird eine Verbindung zur der Datenbank aufgebaut. Welche Verbindung genutzt werden soll wird durch die $GLOBALS['env'] bestimmt.

Beim testen von Methoden/Funktionen die mit der Datenbank operieren, sollte ein "Schalter" implementiert werden, welcher zwischen den Datenbanken wechselt, da PHPUnit einen "truncate" auf die jeweilige Tabelle durchführt.

##PHPUnit - Das test Framework

PHPUnit versorgt uns mit nützlichen Funktionen. Damit können wir eine Verbindung zur einer Test Datenbank herstellen. Wir können unsere Tabellen mit Daten füllen.Mit der Datenbank API von PHPUnit können wir unsere Methoden/Funktionen gegenprüfen.

Arbeitsweise von PHPUnit:

###1. Datenbank-Tabelle leeren

In diesem Prozess werden die zu prüfenden Tabelle geleert.

###2. Daten neu laden

Daten werden in die jweilige Tabelle geladen.

###3. tests werden durchlaufen

Die testes werden nach und nach durchlaufen. Einzelne tests sind unabhänig voneinander, wenn du in einem test Daten löscht, werden diese Daten im nächsten test wieder hergestellt. Kurz gesagt, es besteht keine Pipeline zwischen den tests.

##PHPUnit Datenbank Erweiterung installieren Wir erstellen in unserem Projekt Ordner eine composer.json mit folgenden Inhalt

{
    "require-dev":{
        "phpunit/phpunit": "4.7.*",
        "phpunit/dbunit": ">=1.2"
    }
}

Mit dem Befehl composer install installieren wir alle benötigten Packages. Mit vendor/bin/phpunit tests/*Test können wir unseren Test starten.

##Die Datenbank Erweiterung initialisieren Wir erstellen in unserem tests Ordner eine neue PHP Datei: DatabaseTest.php.

<?php
 
include "config.php";
include "classes/Person.php";
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
    //…
}

Die "PHPUnit_Extensions_Database_TestCase" bietet uns zwei abstrakte Methoden an: "getConnection()" und "getDataSet()". Diese müssen wir überschreiben, sonst bekommen wir eine Fehlermeldung.

###getConnection()

Hier können wir eine Verbindung zur unserer Datenbank aufbauen. Dabei verwenden wir die PDO Library von PHP.

###getDataSet()

Hier können wir unsere test Datenbank und dessen Tabellen, die Daten übergeben. Die Daten können in verschiedenen Formaten übergeben werden: XML, YAML, CSV. Ich habe ich für ein PHP Array entschieden.

<?php
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
     
    static private $pdo = null;
    private $conn = null;
     
    /**
     *
     */
    public function getConnection()
    {
 
        if ($this->conn === null)
        {
            if (self::$pdo == null)
            {
                self::$pdo = Database::connect();
            }
 
            $this->conn = $this->createDefaultDBConnection(self::$pdo, "person");
        }
         
        return $this->conn;
         
    }
     
    /**
     *
     */
    public function getDataSet()
    {
         
        // Test Daten   
        return $this->createArrayDataSet(array(
     
            'person' => array(
                 
                array('id' => 1, 'firstName' => 'John', 'lastName' => 'Doe', 'age' => 24),
                array('id' => 2, 'firstName' => 'Max', 'lastName' => 'Mustermann', 'age' => 17),
                array('id' => 3, 'firstName' => 'Sina', 'lastName' => 'Forest', 'age' => 20)
         
            )
         
        ));
     
    }
}

##Tabellen erzeugen PHPUnit erstellt für uns keine Tabellen, entweder erstellen wir diese händisch ( z.B mySQLWorkbench ) oder wir erstellen eine eigene Methode. Wenn wir eine eigene Methode besitzen können wir diese in der setUp() Methode aufrufen. Die setUp Methode wird von PHPUnit automatisch aufgerufen.

<?php
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
     
    /**
     *
     */
    public function setUp()
    {
         
        $GLOBALS['env'] = 'test';
        $person = new Person();
        $person->create_table();
         
        parent::setUp();
 
    }
}

Reihenfolge der Methoden aufrufe:

  • setUp()
  • getConnection()
  • getDataSet()
  • testIrgendeinTest()

Wenn testIrgendeinTest() fertig ist wiederholt sich das ganze bis es keine tests mehr gibt.

  • setUp()
  • getConnection()
  • getDataSet()
  • testIrgendeinTest2()

Bis alle tests abgeschlossen sind.

##Methoden testen Nachdem wir alles erledigt haben können wir zum wesentlichen übergehen.

###testGetPerson Unser erster Test überprüft die "findOne" Methode, es sollte uns ein Array zurückliefern.

<?php
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
     
    /**
     *
     */
    public function testGetPerson()
    {
 
        $person = new Person();
        $result =  $person->findOne("id = 3");
        $this->assertEquals(array('id' => 3, 'firstName' => 'Sina', 'lastName' => 'Forest', 'age' => 20), $result);
 
    }
}

###testRemovePerson Danach versuchen wir eine Person zu löschen, dadurch soll sich die Anzahl der Personen um eine verringern.

<?php
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
     
    /**
     *
     */
    public function testRemovePerson()
    {
     
        $person = new Person();
        $person->removeByID("1");
        $this->assertEquals(2, $this->getConnection()->getRowCount('person'));
     
    }
}

###testAddPerson Wenn wir eine weitere Person hinzufügen sollte sich die Anzahl der Personen um eine vergrößern.

<?php
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
     
    /**
     *
     */
    public function testAddPerson()
    {
         
        $person = new Person();
        $person->attributes['id'] = 4;
        $person->attributes['firstName'] = "Jakob";
        $person->attributes['lastName'] = "Neumann";
        $person->attributes['age'] = 17;
        $person->add();
        $this->assertEquals(4, $this->getConnection()->getRowCount('person'));
 
    }
}

###testUpdatePerson Wir ändern das Alter einer Person und überprüfen ob sich das Alter wirklich verändert hat. Indem wir ein Gegencheck mit createQueryTable() machen.

<?php
 
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
     
    /**
     *
     */
    public function testUpdatePerson()
    {
 
        $person = new Person();
        $person->update("age=21", " id = 3");
         
        $query = $this->getConnection()->createQueryTable(
            'person', 'SELECT age FROM person WHERE id = 3'
        );
         
        $this->assertEquals($query->getRow(0), array( 'age' => 21 ));
 
    }
}

##PHPUnit Datenbank API PHPUnit stellt uns einige Hilfsfunktionen zur Verfügung, mit diesen können wir unsere Funktionen gegenprüfen.

  • getRowCount('tablename')

Gibt die Anzahl der Zeilen aus.

  • createQueryTable('SELECT * FROM …')

Damit können wir eigene SQL Queries schreiben. Als Rückgabewert bekommen wir die "PHPUnit_Extensions_Database_DataSet_QueryTable" Klasse zurück.

Test durchführen mit: vendor/bin/phpunit tests/DatabaseTest.php

PHPUnit 4.7.3 by Sebastian Bergmann and contributors.
 
....
 
Time: 161 ms, Memory: 5.00Mb
 
OK (4 tests, 4 assertions)
Zurück