ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Syntaxe YAML des tests unitaires dbt

Référence complète pour la structure YAML des tests unitaires dbt — éléments requis, formats d'entrée (dict, csv, sql), configuration optionnelle et fonctionnalités spécifiques aux versions.

Planté
dbttesting

Chaque test unitaire dbt réside dans un fichier YAML avec quatre éléments requis : un nom, un modèle cible, des entrées mockées (given) et la sortie attendue (expect). Il existe trois formats d’entrée et plusieurs options de configuration optionnelles.

Éléments requis

Chaque test unitaire nécessite exactement quatre éléments : un nom, un modèle cible, des données d’entrée (given) et une sortie attendue (expect).

unit_tests:
- name: test_customer_status_logic # Identifiant unique
model: mrt__core__customers # Modèle testé
given: # Entrées mockées
- input: ref('base__crm__customers')
rows:
- {customer_id: 1, status: "active"}
expect: # Sortie attendue
rows:
- {customer_id: 1, is_active: true}

La section given accepte trois types d’entrées :

  • ref() pour les modèles — le cas le plus courant
  • source() pour les tables sources
  • this pour les auto-références dans les modèles incrémentaux

Une commodité cruciale : vous n’avez besoin de spécifier que les colonnes que votre logique utilise réellement. Si base__crm__customers a 30 colonnes mais que votre test ne s’intéresse qu’à customer_id et status, ces deux colonnes suffisent. dbt gère le reste en remplissant les valeurs par défaut.

Formats d’entrée

dbt supporte trois formats pour définir les données de test. Chacun a son domaine d’application.

Format dict

Le format par défaut et le plus lisible pour les petits jeux de données. Chaque ligne est un dictionnaire YAML :

given:
- input: ref('base__shopify__orders')
format: dict
rows:
- {order_id: 1, amount: 100.00, status: "completed"}
- {order_id: 2, amount: 50.00, status: "pending"}

Le format dict est celui que vous utiliserez 90 % du temps. Il est concis, facile à parcourir et s’intègre bien aux diffs de revue de code. Le seul inconvénient est la dégradation de la lisibilité lorsque les lignes ont de nombreuses colonnes — à ce stade, les lignes YAML deviennent longues.

Format CSV

Mieux adapté aux jeux de données plus volumineux ou lorsque vous voulez partager des fichiers de fixtures entre plusieurs tests :

given:
- input: ref('base__shopify__orders')
format: csv
rows: |
order_id,amount,status
1,100.00,completed
2,50.00,pending

Le format CSV supporte aussi les fichiers de fixtures externes, ce qui est utile lorsque le même jeu de données est nécessaire dans plusieurs tests :

given:
- input: ref('base__shopify__orders')
format: csv
fixture: order_test_data

Cela recherche tests/fixtures/order_test_data.csv dans votre projet. Les fixtures externes gardent vos fichiers YAML propres lorsque les données de test sont volumineuses, mais elles ajoutent de l’indirection — quelqu’un lisant le test doit ouvrir un fichier séparé pour voir les entrées.

Format SQL

Requis dans deux situations spécifiques : les modèles éphémères et les scénarios de tables vides.

given:
- input: ref('base__shopify__orders')
format: sql
rows: |
select 1 as order_id, 100.00 as amount, 'completed' as status
union all
select 2 as order_id, 50.00 as amount, 'pending' as status

Pour tester les scénarios à zéro ligne (que se passe-t-il lorsqu’une table est vide ?), le format SQL est la seule option :

given:
- input: ref('base__shopify__orders')
format: sql
rows: |
select
cast(null as int64) as order_id,
cast(null as float64) as amount
where false

L’astuce where false crée un jeu de résultats avec le schéma correct mais zéro ligne. C’est essentiel pour tester les modèles qui doivent gérer gracieusement les tables amont vides.

Une mise en garde importante avec le format SQL pour les modèles éphémères : vous devez inclure TOUTES les colonnes que le modèle référence, pas seulement celles pertinentes pour votre test. Les modèles éphémères ne peuvent pas être requêtés pour leur schéma, dbt n’a donc aucun moyen de remplir les valeurs par défaut.

Configuration optionnelle

Au-delà des bases, les tests unitaires supportent description, tags, meta et activation conditionnelle :

unit_tests:
- name: test_revenue_calculation
model: mrt__finance__orders
description: "Valide le calcul du revenu brut incluant la taxe"
config:
tags: ["critical", "finance"]
meta:
owner: "data-team"
ticket: "DATA-1234"
enabled: "{{ target.name != 'prod' }}" # v1.9+ uniquement
given:
- input: ref('base__shopify__orders')
rows:
- {order_id: 1, subtotal: 100.00, tax_rate: 0.08}
expect:
rows:
- {order_id: 1, gross_revenue: 108.00}

Les tags sont particulièrement utiles. Ils permettent des exécutions sélectives (dbt test --select tag:critical) et facilitent l’exécution des seuls tests unitaires pertinents à un domaine spécifique. Si vous étiquetez les tests par domaine métier (finance, marketing, core), les équipes peuvent n’exécuter que leurs tests pendant le développement.

La configuration enabled permet de sauter des tests dans certains environnements. C’est une fonctionnalité dbt 1.9+ — elle ne fonctionnera pas sur la 1.8.

Le bloc meta est libre. Utilisez-le pour la propriété (owner), la traçabilité (ticket) ou toute métadonnée spécifique à votre projet dont votre équipe a besoin.

Compatibilité des versions

La syntaxe des tests unitaires a évolué entre les versions dbt, et les différences importent :

  • dbt 1.8 (mai 2024) : Introduction des tests unitaires. La clé tests: a été renommée en data_tests: pour éviter l’ambiguïté avec les tests unitaires. Si vous mettez à niveau depuis une version antérieure à la 1.8, renommez tous les blocs tests: de votre YAML en data_tests:.
  • dbt 1.9 : Ajout de l’option de configuration enabled pour l’exécution conditionnelle des tests. Nouveaux flags --resource-type et --exclude-resource-type pour le filtrage.
  • dbt 1.11 : Les tests unitaires pour les modèles désactivés sont maintenant automatiquement désactivés — plus de tests orphelins qui échouent sur des modèles qui ont été désactivés.

Le renommage data_tests: surprend les équipes pendant les mises à niveau. Si vous observez un comportement inattendu après être passé à la 1.8+, vérifiez si vous avez encore des blocs tests: qui nécessitent d’être renommés.

Choisir un format

Pour la plupart des équipes, la décision est simple :

  • Format dict pour tout ce qui fait moins de ~10 lignes par entrée (ce qui est presque tous les tests unitaires)
  • Fixtures CSV lorsque le même grand jeu de données est réutilisé dans plusieurs tests
  • Format SQL uniquement quand dict/csv ne peut pas fonctionner — modèles éphémères et tables vides

Évitez de mélanger les formats au sein d’un même test sans raison forte. La cohérence rend les tests plus faciles à lire et à réviser.

Le bloc expect supporte les mêmes formats que given, bien que le format dict soit presque toujours le bon choix là-dedans — les sorties attendues sont généralement petites, et vous voulez qu’elles soient visibles inline pour une comparaison rapide.