Au fil des flows

22août/135

Tester une application adossee a une base de donnee avec PHP Unit

Hier, j'ai écrit pour Vamos à la playa, une série de tests automatisés pour valider la partie serveur.

Au moment où j'écris ce billet, cette partie compte seulement 4 classes et un routeur. La partie serveur expose une API Restful qui compte une dizaine d'opération.

Le problème que je rencontrai depuis le départ, c'est quoi tester ? 90% du travail d'exposition des données consistent à récupérer les résultats en base et à les exposer au format json.

Qu'est ce que je teste ?

C'est naturellement que j'ai commencé à créer un projet de tests unitaires sous PHP Unit. J'ai réfléchi à 2 solutions :

  1. Encapsuler la récupération des données et écrire un bouchon pour les tests
  2. Considérer la base de donnée comme un composant logiciel avec un état

La solution 1 me posait un problème. Actuellement, Vamos est adossé à une base mysql. Vu le type d'application, c'est un choix raisonnable au départ du projet. C'est une techno maîtrisée et facile à mettre en oeuvre. A terme, si l'API évolue et que le projet grandit, il faudra envisager d'autres solutions techniques. La façon dont j'accède aux données présage nullement de la façon dont j'y accéderai demain. Si je conçois la partie DAO comme une partie indépendante et que je fige les interfaces, je fais un choix d'architecture prématurée.

Il reste donc la solution 2.

La base de donnee, un composant logiciel comme un autre

Vous vous demandez surement ce que j'entend par composant logiciel ? Un composant logiciel est une vue de l'esprit. Il traite des requêtes (qu'on peut généraliser par message) et modifie son état interne.

La base de donnée est un composant logiciel de mon application :

  • Elle exécute les requêtes de celle ci (SQL)
  • Elle modifie son état interne en conséquence

Si je ne maîtrise pas l'état de ce composant, aucune chance de pouvoir tester mon application.

Principe du test logiciel

Pour effectuer du test automatisé, un composant peut soit être remplacé par un bouchon (cas d'un test unitaire), soit être initialisé dans un état cohérent (cas pour un test fonctionnel).

J'ai écarté la première solution. Comment mettre en oeuvre la seconde de manière efficace ?

Le module DbUnit de PHPUnit

D'après ce que j'ai constaté, ce module répond à 2 besoins :

  • Initialiser une base de donnée à l'état voulu
  • Comparer l'état d'une base de donnée à un état attendu

Pour cela, le système s'appuie sur le concept de Dataset. Un dataset est une vue simplifiée des enregistrements présents dans les tables d'une base de donnée.

Ce concept s'appuie sur un autre format que le sql pour être plus facile à traiter.

Concrètement, voici un dataset au format xml :

<?xml version="1.0" encoding="UTF-8"?>
<!--
Dataset contains :
 * 2 playas
 * 4 olas
-->
<dataset>
  <table name="valp_beaches">
    <column>id</column>
    <column>name</column>
    <column>city</column>
    <column>latitude</column>
    <column>longitude</column>
    <column>modified</column>
    <column>created</column>
    <row>
      <value>1</value>
      <value>Playa 1</value>
      <value>City 1</value>
      <value>45.0</value>
      <value>45.0</value>
      <value>2013-08-24 17:15:23</value>
      <value>2013-04-24 17:15:23</value>
    </row>
    <row>
      <value>2</value>
      <value>Playa 2</value>
      <value>City 2</value>
      <value>46.0</value>
      <value>46.0</value>
      <value>2013-09-24 17:15:23</value>
      <value>2013-09-24 17:15:23</value>
    </row>
  </table>
  <table name="valp_beaches_gps" />
  <table name="valp_beaches_ondas">
    <column>id</column>
    <column>message</column>
    <column>created</column>
    <row>
      <value>1</value>
      <value>J'ai vu un requin</value>
      <value>2013-09-29 17:15:23</value>
    </row>
    <row>
      <value>2</value>
      <value>Ola 2</value>
      <value>2013-09-29 17:16:23</value>
    </row>

  </table>
</dataset>

Php unit utilise ce dataset pour :

  • Vider les tables qui y sont référencés
  • Les peupler avec les données

Pour utiliser cette feature, vous devez créer une classe de test qui hérite de PHPUnit_Extensions_Database_TestCase

class DatabaseTest extends \PHPUnit_Extensions_Database_TestCase {

  protected function getConnection() {
    $conf_database = array('connection_string' => 'mysql:host=127.0.0.1;dbname=mabase',
                            'user' => 'mabase_user',
                            'password' => 'mabase_password');

    $pdo = new \PDO($conf_database['connection_string'], 
            $conf_database['user'], 
            $conf_database['password'], 
            array(\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
    $pdo -> setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    
    return $this->createDefaultDBConnection($pdo, ':memory:');
  }

  protected function getDataSet() {
    return $this->createXmlDataSet('datasets/playa_2_ola_2.xml');
  }

  public function testReadAll() {
    // ... Votre test
  }
}

J'ai simplifié ma classe de tests pour ce billet. Je vous renvoie à la documentation officielle pour avoir plus d'information sur les bonnes pratiques dans l'initialisation de votre base.

Avant d'exécuter le test DatabaseTest::testReadAll, PHP Unit initialisera la base avec les données provenant de notre dataset.

Pour conclure

Le mécanisme offert par PHP Unit permet de comparer une base après une opération qui a modifiée les données qu'elle contient avec un dataset.

A noter que sans utiliser cette extension, vous pouvez tout à fait initialiser votre base en utilisant un script sql exécuté dans la méthode setUp de votre classe de test. Cette solution vous privera de la possibilité de comparer le contenu de votre base avec un fichier dataset.

Posted by Fabien Arcellier

Remplis sous: Non classé Laisser un commentaire
Commentaires (5) Trackbacks (0)
  1. Wow, that’s what I was looking for, what a data! existing
    here at this weblog, thanks admin of this web site.

  2. Hi there to every , because I am actually eager of reading
    this webpage’s post to be updated daily.
    It contains good stuff.

  3. I pay a visit each day some blogs and sites to read posts, except this webpage presents quality based writing.

  4. Hey very nice web site!! Man .. Excellent .. Wonderful .. I will bookmark your web site and
    take the feeds additionally? I am happy to find numerous helpful info right here in the submit, we need work
    out more techniques in this regard, thank you for sharing.

    . . . . .

  5. It’s awesome designed for me to have a site, which is beneficial for my experience.

    thanks admin


Leave a comment

Aucun trackbacks pour l'instant