vendredi 4 octobre 2013

Test du data warehouse - partie 1

Quand on parle de business intelligence et de data warehouse, c'est souvent en terme d'analyse de données métier, de rapports à fournir ou de KPI. Il me semble qu'on oublie souvent la partie technique de conception du data warehouse et de réalisation des traitements d'intégration et d'agrégation des données.

Comme tout développement classique, ces aspects nécessitent d'être testés. Je vais profiter de cet article pour partager mes réflexions sur le test des applications Data Warehouse. Évidemment, quand je parle de test, c'est de test automatisé qu'il s'agit.

Cet article se cantonnera au test des traitements d'intégration. Pour faciliter votre digestion, je parlerai des tests du modèle de données une autre fois.

Spécificités du domaine du Data Warehouse

Selon moi, le domaine du data warehouse présente les spécificités suivantes à prendre en compte pour élaborer des stratégies de test pertinentes.

  • Ils récupèrent des données de plusieurs applications du SI, si ce n'est de tout le SI. Les interfaces avec ces applications ont été construites progressivement et ont évolué avec les évolutions ou changements des applications partenaires. Les contrats d'interface n'ont donc pas forcément gardé leur cohérence au fil du temps et nous devons nous attendre à ce que des données similaires (comme des identifiants client ou produit) nous arrivent avec des formats différents.
  • Les reprises de données sont couteuses. Un data warehouse est censé stocker les données avec un historique de plusieurs années, ce qui représente rapidement plusieurs centaines de gigaoctets voire de téraoctets. Un changement dans les traitements d'intégration ou dans le modèle de données peut s'avérer complexe à mettre en place s'il implique une reprise d'historique. Cela signifie également qu'une anomalie dans l'un ou l'autre de ses aspects représentera un coût important. La stratégie de tests devra donc couvrir les aspects traitement et modèle d'une façon suffisamment large pour limiter au maximum tout comportement indésirable.

Test des traitements d'intégration

Par traitement d'intégration, je veux parler des traitements Extract Transform Load (ETL) ou Extract Load Transform (ELT). Même s'il existe des outils du marché pour réaliser ce type de traitement il est possible d'automatiser le test à plusieurs niveau, comme pour un développement classique.

Sur mon projet actuel, l'intégration se fait par traitements ELT en PLSQL dans une base de données Oracle. Il existe plusieurs frameworks de test dans cette techno mais j'ai choisi d'utiliser Java et JUnit pour les raisons suivantes :

  • Java est une techno souvent connue des développeurs, je ne dois pas passer trop de temps former les membres de l'équipe pour qu'ils puissent coder des tests.
  • La programmation orientée objet offre des facilités pour écrire et factoriser du code.
  • Les IDE Java sont très puissants et permettent d'éditer le code très rapidement.

Dernier argument, les rapports JUnit sont lisibles par Jenkins sans configuration particulière, ce qui peut permettre de ne pas trop se casser la tête pour afficher les résultats de tests lors de builds automatisés.

Si vous utilisez un outil comme Talend ou autres Ab Initio, j'imagine qu'il est possible de construire des graphes de tests qui vous permettrons de tester vos traitements (je comptais essayer la démarche sur AI... avant de changer de projet).

Difficultés posées par la démarche

Vouloir tester un data warehouse pose la question du choix du niveau de précision, c'est à dire de sa position dans la pyramide de tests. Ce problème est posé par la quantité importante de cas possibles présentés par les données réelles et la forte probabilité de tomber sur des cas inattendus pour lesquels nous n'avons pas mis en place de règle métier.

La première stratégie est de favoriser le test sur les échantillons réels. La démarche est alors d'injecter, traiter et valider les traitements sur une quantité importante de données. Dans les discussions que j'ai pu avoir dans les équipes projets, c'est la stratégie qui est le plus souvent abordée. Instinctivement, on pense en effet que tester un data warehouse nécessite de lui envoyer beaucoup de données.

Les points positifs de cette stratégie sont que statistiquement, nous sommes quasiment sûr de retrouver tous les cas possibles pour peu que nous prenions une quantité suffisante de données, comme par exemple une journée de transactions. Les données réelles permettent d'échanger facilement avec le client sur ce qu'il connait pour trouver des solutions aux problèmes rencontrés sur des cas non spécifiés ou dans des anomalies compliquées à cerner. Elle permettent également de jouer des tests quantitatifs pour dire, en synthèse, si un traitement semble fonctionner. Par exemple, si vous trouvez en traitant journée de transactions un chiffre d'affaire de 20 000 € alors que le client gagne en moyenne 100 000 € par jour, c'est certainement que vous avez oublié de traiter quelques enregistrements...

Cette approche pose également quelques problèmes. Premièrement, la vérification des résultats est difficilement automatisable. En effet, nous ne pouvons pas avoir une connaissance exhaustive des données en entrée du traitement et la plupart du temps le client ne peut pas nous donner un résultat complet attendu. Dans le cas où l'automatisation est possible les tests restent longs. Cela pose des problèmes sur l'organisation des tâches de développement : je ne veux pas qu'un développeur passe 30 minutes à se tourner les pouces en attendant de pouvoir vérifier le résultat de son traitement ni qu'il se sente obligé de paralléliser plusieurs tâches pour éviter de perdre du temps (confer la tonne d'articles sur le fait que l'humain n'est pas multitâche).

En réponse, je préfère évidemment une approche micrométrique et un développement dirigé par les tests (TDD). Ceci nécessite de tester au niveau des fonctions chaque règle métier, chaque combinaison possible. La difficulté pour les développeurs est de prendre le temps de se constituer un jeu de données fictif minimal couvrant toutes les combinaisons et les cas d'erreurs. D'un autre côté, s'il est difficile de se constituer un jeu de données, c'est peut-être le signe d'un problème de conception.

Le bénéfice de cette approche est d'offrir beaucoup de confort en phase de réalisation. Je ne peux cependant pas soutenir qu'il s'agisse de la silver bullet du test : elle porte son lot de points négatifs. Elle nécessite notamment une attention particulière lors du recueil du besoin. Pour cela, le développeur se doit d'avoir une connaissance fine des sources de données pour savoir demander au client comment traiter tel ou tel cas retors. D'autre part, il est difficile de justifier de la capacité de ces tests à valider l'application puisque nous les avons volontairement éloignés de la réalité fonctionnelle (les cas de test sont identifiables par les développeurs et minimaux).

Le mieux je pense est d'associer les deux approches en faisant 80% de tests micrométriques et 20% de tests sur les données réelles. La vérification sur les données réelles se fait d'une part en prenant un petit échantillon pour une vérification qualitative et d'autre part par une vérification quantitative se basant sur des invariants ou des résultats attendus éventuellement vérifiables sur une source tierce comme l'application émettrice des données.

Pourquoi parler de tests micrométriques et pas de tests unitaires ? Les tests unitaires ne sollicitent pas d'interface, ce qui les rend très rapides. Ici, les tests utilisent la base de données. Avec un produit ETL, vous utiliserez certainement des fichiers ou ferez des look ups en base de données. Les tests dont je parle durent difficilement moins d'une seconde contrairement aux quelques centièmes attendus dans une approche TDD habituelle.

Conseils pour une approche de tests micrométriques

  • Mettez en place un jeu de donnée minimal pour tester une fonction ou une règle données : il sera plus compréhensible et maintenable par la suite.
  • Assurez-vous tout de même de tester sur plusieurs enregistrements, surtout si vous faites du développement spécifique ; il m'est déjà arrivé de commiter du code pour lequel chaque fonction était super testée... mais où j'avais oublié d'écrire la boucle for nécessaire au traitement de tous les enregistrements !
  • Faites en sorte que votre jeu de données soit chargé en base ou lu qu'une fois dans vos test : vous gagnerez du temps sur votre suite de test et résisterez plus facilement à switcher sur Twitter, Facebook ou Youporn.
  • Suivez le principe du TDD : écrivez un test en échec avec votre jeu de données minimal, lancez le pour le voir échouer et confirmer qu'il fonctionne. Codez jusqu'à que le test soit validé. Refactorisez si nécessaire et recommencez en enrichissant votre jeu de données.
  • Outillez-vous pour que votre boucle test - code - refactoring soit la plus rapide possible.