Aller au contenu principal
Tester ses données comme on teste son code, avec dbt
Retour
Data15 min de lecture

Tester ses données comme on teste son code, avec dbt

Guillaume HERMANGuillaume HERMAN|Mai 2026

La data quality n’est pas un problème technique, c’est un problème de confiance

Un dashboard qui affiche un chiffre d’affaires faux une seule fois, c’est une équipe métier qui ne fait plus confiance aux données pendant six mois. Le directeur commercial qui a présenté un pipeline gonflé de 15 % en comité de direction ne l’oubliera pas de sitôt. Et quand la confiance est perdue, les équipes reviennent à Excel : le pire scénario possible pour votre investissement data

Le problème est massif. Selon Gartner, le coût de la mauvaise qualité des données s’élève en moyenne à 12,9 millions de dollars par an pour une grande entreprise. Pour une ETI, les ordres de grandeur sont moindres mais les conséquences proportionnellement plus lourdes : une erreur de reporting réglementaire peut entraîner une amende, un audit prolongé, ou une perte de crédibilité auprès des parties prenantes

Et pourtant, la majorité des équipes data ne testent pas leurs données. Pas par négligence, par manque d’outillage adapté. Jusqu’à l’émergence de dbt, tester ses données nécessitait des scripts Python ad hoc, des jobs de validation manuels, ou des outils spécialisés coûteux et complexes

Le parallèle avec le monde logiciel est frappant. Les développeurs ont résolu ce problème il y a vingt ans avec le TDD (Test-Driven Development) et l’intégration continue. Un code qui n’est pas testé n’est pas livré, c’est un principe acquis. Il est temps d’appliquer la même rigueur aux données

dbt rend cette rigueur accessible sans infrastructure supplémentaire : les tests vivent dans le projet, aux côtés des modèles qu’ils protègent

Les tests natifs dbt

dbt intègre nativement un framework de testing déclaratif. Vous déclarez vos tests en YAML, dans les mêmes fichiers que votre documentation, et ils s’exécutent avec une seule commande : dbt test

Les 4 tests génériques

# models/marts/_marts__models.yml

models:
  - name: fct_commandes
    columns:
      - name: commande_id
        tests:
          - unique
          - not_null
      - name: client_id
        tests:
          - not_null
          - relationships:
              to: ref('dim_clients')
              field: client_id
      - name: statut
        tests:
          - accepted_values:
              values: ['validee', 'annulee', 'en_cours', 'livree']
      - name: montant_ttc
        tests:
          - not_null

Quatre tests, quatre garanties fondamentales :

  • unique : chaque valeur de commande_id est unique. Si deux lignes ont le même ID, quelque chose a mal tourné dans votre pipeline (duplication, mauvais GROUP BY, absence de déduplication en staging).
  • not_null : la colonne ne contient pas de valeurs nulles. Un client_id null dans une table de commandes, c’est une commande orpheline qui faussera tous vos agrégats par client.
  • relationships : chaque client_id dans fct_commandes existe dans dim_clients. C’est l’équivalent de la contrainte de clé étrangère, mais testée au niveau de la couche analytique.
  • accepted_values : le statut ne peut prendre que 4 valeurs. Si une cinquième apparaît ("annulé" avec un accent, ou "VALIDEE" en majuscules), le test échoue et vous le savez immédiatement

Ces quatre tests couvrent 80 % des erreurs de données courantes. Ils sont gratuits, triviaux à écrire, et s’exécutent en quelques secondes

Tests custom SQL

Pour les règles métier spécifiques, dbt permet d’écrire des tests en SQL pur. Un test custom est une requête qui retourne les lignes en erreur, si la requête retourne zéro ligne, le test passe :

-- tests/assert_coherence_montants.sql
-- Le montant TTC doit toujours être supérieur ou égal au montant HT

select
    commande_id,
    montant_ht,
    montant_ttc
from {{ ref('fct_commandes') }}
where montant_ttc < montant_ht
-- tests/assert_pas_de_commandes_futures.sql
-- Aucune commande ne peut avoir une date dans le futur

select
    commande_id,
    date_commande
from {{ ref('fct_commandes') }}
where date_commande > current_date

Les tests custom sont puissants parce qu’ils expriment des règles métier que seuls les analystes connaissent. “Le montant TTC est toujours supérieur au montant HT” n’est pas un test technique, c’est une invariant métier. Si elle est violée, c’est qu’un cas d’usage n’a pas été anticipé (remboursement, avoir, erreur de saisie)

Generic vs singular vs custom : quand utiliser quoi

Type Quand l’utiliser Exemple
Generic (unique, not_null, etc.) Sur chaque modèle, systématiquement Clés primaires, colonnes obligatoires
Singular (fichier SQL dédié) Règles métier spécifiques à un modèle Cohérence montant HT/TTC
Custom generic (macro réutilisable) Règles métier réutilisées sur plusieurs modèles “Colonne positive”, “Date pas dans le futur”

Monter en puissance : dbt-expectations et elementary

Les tests natifs couvrent les fondamentaux. Pour une couverture avancée, deux packages communautaires dominent l’écosystème

dbt-expectations : le Great Expectations de dbt

dbt-expectations porte dans dbt les tests du framework open-source Great Expectations : plus de 50 tests avancés, déclarés en YAML comme les tests natifs

# Exemples de tests dbt-expectations sur un modèle financier

models:
  - name: fct_factures
    columns:
      - name: montant_ttc
        tests:
          - dbt_expectations.expect_column_values_to_be_between:
              min_value: 0
              max_value: 1000000
              row_condition: "statut != 'avoir'"
          - dbt_expectations.expect_column_mean_to_be_between:
              min_value: 100
              max_value: 5000
      - name: date_facture
        tests:
          - dbt_expectations.expect_column_values_to_be_between:
              min_value: '2020-01-01'
              max_value: '{{ modules.datetime.date.today().isoformat() }}'
      - name: devise
        tests:
          - dbt_expectations.expect_column_distinct_count_to_equal:
              value: 3 # EUR, USD, GBP
    tests:
      - dbt_expectations.expect_table_row_count_to_be_between:
          min_value: 1000
          max_value: 500000

Les tests dbt-expectations sont particulièrement utiles pour :

  • Les bornes de valeurs : un montant entre 0 et 1M€, une date dans une plage plausible
  • Les distributions statistiques : la moyenne d’une colonne doit rester dans une fourchette
  • Les comptages : le nombre de lignes attendu, le nombre de valeurs distinctes
  • Les cohérences inter-colonnes : la colonne A est toujours supérieure à la colonne B

elementary : l’observabilité data

elementary va au-delà des tests ponctuels, il ajoute une couche d’observabilité continue à votre projet dbt

Les fonctionnalités clés :

  • Détection d’anomalies automatique : elementary analyse l’historique de vos données et alerte quand un pattern change (volume de lignes inhabituellement élevé ou bas, valeur moyenne qui dévie, freshness en retard).
  • Dashboard intégré : un site statique (comme dbt docs) qui affiche l’état de santé de vos données, tests passés/échoués, anomalies détectées, tendances.
  • Alertes Slack/email : notifications automatiques quand un test échoue ou qu’une anomalie est détectée.
  • Lineage enrichi : visualisation des dépendances avec l’état de santé de chaque modèle

elementary ne remplace pas les tests dbt, il les complète. Les tests dbt valident des règles explicites (“cette colonne est unique”). elementary détecte des anomalies implicites (“ce mois-ci, il y a 3 fois plus de lignes que d’habitude, est-ce normal ?”)

Coût d’elementary en pratique : le package open-source est gratuit en licence, mais ses matérialisations (anomaly detection, freshness, volume) exécutent des requêtes qui consomment les crédits du warehouse : typiquement 30-80 $/mois sur 80 modèles avec un run quotidien, 200-500 $/mois sur 200+ modèles avec un run horaire. Elementary Cloud (la version SaaS payante) démarre à ~50 $/utilisateur/mois et apporte un dashboard et des alertes natifs en plus du package

Comparatif

Critère Tests natifs dbt dbt-expectations elementary
Type de validation Règles explicites simples Règles explicites avancées Détection d’anomalies
Configuration YAML natif YAML (package) YAML + modèles elementary
Nombre de tests 4 génériques 50+ Anomaly detection automatique
Coût Inclus dans dbt Gratuit (open-source) Gratuit en licence (coût warehouse marginal, voir note ci-dessus)
Cas d’usage idéal Fondamentaux (clés, nulls) Règles métier complexes Monitoring continu
Courbe d’apprentissage Très faible Faible Modérée

Notre recommandation : commencez par les tests natifs (jour 1), ajoutez dbt-expectations dès que vous avez des règles métier à valider (semaine 2), et déployez elementary quand vous passez en production (mois 1)

Avec ces trois niveaux d’outils, la question devient : comment les organiser pour que chaque test soit exécuté au bon moment et avec la bonne priorité ?

La pyramide de tests Data

Les équipes logicielles connaissent la pyramide de tests : beaucoup de tests unitaires rapides en bas, peu de tests end-to-end lents en haut. Le même modèle s’applique aux données

Base : tests schema (rapides, exhaustifs)

Les tests not_null et unique sur les clés primaires de tous les modèles. Exécution : quelques secondes par test. Ils attrapent les erreurs grossières : duplications, données manquantes, types incorrects

Objectif : 100 % de couverture sur les clés de tous les modèles

Milieu : tests métier (ciblés, importants)

Les tests accepted_values, relationships, et les tests custom qui valident la logique métier. Ils s’appliquent aux modèles intermediate et marts critiques, pas à tous les modèles

Objectif : couverture complète sur les marts exposés aux utilisateurs et les intermediate qui portent la logique métier complexe

Sommet : tests de réconciliation (lents, stratégiques)

Les tests qui comparent le résultat final avec la source. Par exemple : “la somme du chiffre d’affaires dans fct_commandes_mensuelles est égale à la somme dans le système ERP source, à 0,1 % près.” Ces tests sont lents (ils requêtent les sources) mais détectent les erreurs systémiques

-- tests/reconciliation_ca_erp.sql

with dbt_total as (
    select sum(chiffre_affaires) as ca_dbt
    from {{ ref('fct_commandes_mensuelles') }}
    where mois >= date_trunc('month', current_date - interval '1 month')
),

erp_total as (
    select sum(total_amount) as ca_erp
    from {{ source('erp', 'orders') }}
    where order_date >= date_trunc('month', current_date - interval '1 month')
      and status = 'validated'
)

select
    d.ca_dbt,
    e.ca_erp,
    abs(d.ca_dbt - e.ca_erp) / nullif(e.ca_erp, 0) as ecart_pct
from dbt_total d
cross join erp_total e
where abs(d.ca_dbt - e.ca_erp) / nullif(e.ca_erp, 0) > 0.001  -- seuil 0,1%

Objectif : 1 à 3 tests de réconciliation sur les KPI les plus critiques (chiffre d’affaires, volumes, indicateurs réglementaires)

Exécution sélective

dbt permet de tagger les tests par priorité et de les exécuter sélectivement :

# Tous les tests (CI complet)
dbt test

# Uniquement les tests critiques (pre-merge)
dbt test --select tag:critical

# Tests sur un modèle spécifique (développement)
dbt test --select fct_commandes_mensuelles

Le tagging par priorité (critical, standard, reconciliation) permet d’adapter la durée de la pipeline CI au contexte : tests critiques en 30 secondes pour les PR, suite complète en 5 minutes pour le merge, réconciliation en nightly

Structurer les tests par couches ne suffit pas, encore faut-il que les échecs déclenchent une réaction avant que quiconque ne consulte un dashboard

Alerting et monitoring en production

Les tests ne servent à rien s’ils ne tournent pas. En production, deux stratégies se complètent

Tests bloquants en CI/CD

Avant chaque merge dans la branche principale, la pipeline CI exécute dbt test --select tag:critical. Si un test échoue, le merge est bloqué. C’est le premier filet de sécurité : aucune transformation défectueuse ne passe en production

Tests post-exécution avec alerting

Après chaque dbt build en production (quotidien, horaire, ou événementiel), les tests s’exécutent. Si un test échoue :

  1. elementary détecte l’échec et l’anomalie éventuelle
  2. Une alerte est envoyée sur Slack (canal #data-quality) et par email aux propriétaires du modèle
  3. Le dashboard elementary est mis à jour avec le détail de l’erreur

Ce flux transforme les tests en filet de sécurité opérationnel. Les erreurs sont détectées en minutes, pas en jours. L’équipe métier ne voit jamais un dashboard faux, l’alerte arrive avant que quiconque n’ouvre Metabase En pratique, combien de temps faut-il pour passer d’un projet sans aucun test à une couverture qui change la donne ? Voici ce que nous avons observé chez un client

Retour terrain : de 0 à 150 tests en 2 mois

L’éco-organisme que nous avons accompagné avait un problème récurrent : les équipes métier découvraient les erreurs de données avant l’équipe data. Un reporting mensuel envoyé au régulateur contenait une incohérence que personne n’avait vue. Le déclencheur de notre intervention sur la qualité

Phase 1 (semaine 1-2) : les fondamentaux

Ajout des tests natifs sur tous les modèles existants :

  • unique + not_null sur toutes les clés primaires (82 modèles)
  • accepted_values sur les colonnes de statut (12 colonnes)
  • relationships entre les marts et les dimensions (8 relations)

Résultat : 95 tests créés, dont 7 en échec dès la première exécution. Sept erreurs silencieuses qui vivaient dans la production depuis des semaines : des doublons dans une table de collecte, des statuts mal orthographiés, une clé étrangère cassée

Phase 2 (semaine 3-4) : les tests métier

Ajout de tests custom pour les invariants métier critiques :

  • Cohérence des volumes déclarés (tonnage collecté ≤ tonnage mis sur le marché)
  • Pas de dates dans le futur
  • Montants toujours positifs sauf pour les avoirs
  • Réconciliation mensuelle avec l’ERP sur le chiffre d’affaires

Résultat : 35 tests custom supplémentaires, dont 2 en échec (un cas d’avoir non géré, une date de collecte futuriste issue d’un fichier opérateur mal formaté)

Phase 3 (mois 2) : monitoring continu

Déploiement d’elementary avec alertes Slack. Configuration de la détection d’anomalies sur les 10 tables les plus critiques (volume de lignes, fraîcheur, distributions)

Les résultats :

Au total, 150 tests et règles actives (schema dbt natif, tests custom SQL, monitoring continu elementary)

Métrique Avant Après Variation
Tests en production 0 150 N/A
Incidents data/mois découverts par les métiers 8 0,5 -94 %
Délai de détection d’une erreur 3 jours (moyenne) < 1 heure -99 %
Incidents détectés par les tests avant impact 0 % 95 % N/A
Satisfaction métier (survey interne) 3,2/10 8,1/10 N/A

Note : “< 1 heure” désigne le délai entre l’exécution du dbt build planifié et l’alerte Slack/email. Le délai de correction effective varie de quelques minutes (fichier mal formé détecté par un test schema) à plusieurs heures (règle métier non anticipée à creuser avec le sponsor data).

Le chiffre le plus parlant : 95 % des incidents sont désormais détectés par les tests avant que les métiers ne les voient. Les 5 % restants sont des cas limites que les tests ne couvrent pas encore, et chaque incident non détecté se transforme en un nouveau test. Le filet se resserre à chaque erreur

Ces résultats sont atteignables rapidement, à condition d’éviter les erreurs que nous voyons se répéter d’un projet à l’autre

Les 3 pièges du testing data

1. Tester tout avec la même rigueur. 150 tests qui s’exécutent en 15 minutes, c’est une pipeline bloquée pendant 15 minutes à chaque merge. Priorisez : tests critiques en CI (30 secondes), suite complète en post-merge (5 minutes), réconciliation en nightly (15 minutes). Si vous testez tout avec la même priorité, vous ne testez rien efficacement

2. Des tests qui passent toujours. Un test not_null sur une colonne qui n’est jamais null (par construction) ne vous protège de rien. Avant de déployer un test, demandez-vous : “dans quel scénario réaliste ce test échouerait-il ?”. Si vous ne trouvez pas de scénario, le test est inutile. Pire : il vous donne un faux sentiment de sécurité

3. Ignorer les tests lents. Un test de réconciliation qui prend 10 minutes bloque toute la pipeline si vous le mettez dans le CI. Mais si vous le supprimez, vous perdez une couverture critique. La solution : le tagging. Marquez-le tag:nightly, exécutez-le une fois par jour en dehors des heures de travail. Les résultats sont visibles dans le dashboard elementary, pas besoin qu’il bloque le merge

Conclusion

La qualité des données n’est pas un luxe, c’est le prérequis de la confiance. Sans tests, vos dashboards sont des opinions. Avec des tests, ce sont des faits vérifiés

dbt a démocratisé le testing data en le rendant aussi simple que le testing logiciel : déclaratif, intégré au workflow, exécuté automatiquement. Les packages communautaires (dbt-expectations, elementary) étendent cette couverture aux cas avancés et au monitoring continu

L’investissement est modeste : 2 à 4 semaines pour passer de 0 à une couverture solide, et quelques heures par mois pour maintenir et étendre les tests. Le ROI est immédiat : des erreurs détectées en minutes au lieu de jours, des équipes métier qui retrouvent confiance, et un reporting fiable

Nobori accompagne les ETI dans la mise en place de frameworks de qualité data. Découvrir notre offre Data

Sources et méthodologie

  • Gartner, “The Cost of Poor Data Quality” (2024) : estimation 12,9M$/an pour les grandes entreprises
  • dbt Labs, “Testing best practices” : framework de tests natifs et custom
  • dbt-expectations : package communautaire, port de Great Expectations pour dbt
  • elementary-data : observabilité data open-source pour dbt
  • Retours d’expérience Nobori : données issues de nos missions de qualité data auprès d’ETI (métriques avant/après anonymisées)

Les métriques du retour terrain (éco-organisme) sont issues de mesures réelles sur une période de 3 mois post-déploiement. Le délai de détection “< 1 heure” correspond au temps entre l’exécution du dbt build planifié et l’alerte Slack, pas le délai de correction effective de l’incident, qui dépend de sa nature (de quelques minutes à quelques heures)

Pour aller plus loin

Ce sujet vous concerne ?

Évaluez votre maturité en Data & Analytics en 30 min avec un expert senior. Sans engagement

Guillaume HERMAN

Guillaume HERMAN

Lead Data

AWS SA ProfessionalGCP Pro Cloud ArchitectTerraform Associate

Ce sujet vous intéresse ?

Évaluez votre maturité avec un diagnostic de 30 min.

Newsletter

Restez informé

Analyses Cloud, Data & IA : 1 email par mois, pas plus

Réserver un appel découverte