Zum Hauptinhalt springen
dbt TestsDatenqualität in dbtTesten mit dbtdata build toolValidierung von Daten

dbt Tests: der Leitfaden zu Prüfungen der Datenqualität

dbt Tests sind automatische Zusicherungen an die Datenqualität, die gegen die Tabellen und Views laufen, die Ihre dbt-Modelle bauen. Sie beantworten eine einfache Frage: „Entsprechen die Daten in diesem Modell unseren Erwartungen?“ Ein bestandener Test heißt, dass die Annahme für die aktuellen Daten gilt; ein fehlgeschlagener Test legt ein Problem der Datenqualität offen, bevor es still die nachgelagerten Dashboards, die Merkmale für das maschinelle Lernen oder die Finanzberichte verdirbt.

TL;DR

dbt Tests sind Anweisungen im SQL SELECT, die Zeilen zurückgeben, wenn eine Zusicherung verletzt ist (null Zeilen = bestanden). Es gibt zwei Arten: generische Tests, die in schema.yml erklärt werden (vier eingebaute: not_null, unique, accepted_values, relationships), und singuläre Tests, also eigene Dateien in SQL im Verzeichnis tests/. Pakete aus der Community wie dbt_utils und dbt_expectations ergänzen über 50 weitere Testarten. Die Ergebnisse der Tests fließen in Plattformen für die Governance wie Dawiso, die daraus eine Sicht auf die Datenqualität über den ganzen Katalog hinweg bauen.

Was sind dbt Tests?

dbt bringt ein eingebautes Rahmenwerk für Tests mit, das keine zusätzliche Infrastruktur braucht: Tests sind schlicht Abfragen in SQL, die dbt übersetzt und gegen Ihr Warehouse ausführt. Jeder Test übersetzt zu einer SELECT-Anweisung, die Zeilen zurückgibt, wenn die Zusicherung verletzt ist. Gibt die Abfrage null Zeilen zurück, ist der Test bestanden. Dieser Weg ist aus mehreren Gründen stark:

  • Die Tests laufen in Ihrem Warehouse, es braucht weder zusätzliche Rechenleistung noch zusätzliche Werkzeuge.
  • Die Tests sind Code, sie liegen in der Versionskontrolle neben den Modellen, die sie schützen.
  • Die Tests sind ausführbare Dokumentation, sie legen fest, wie „richtige Daten“ aussehen.
  • Ein fehlgeschlagener Test kann die Pipelines für CI/CD anhalten und verhindern, dass schlechte Daten in die Produktion gelangen.

Ohne automatische Tests kommen Rückschritte in der Datenqualität auf die harte Tour ans Licht: Eine Analystin bemerkt eine Auffälligkeit, jemand aus dem Fachbereich stellt eine Zahl in Frage, oder ein Produkt zeigt den Kunden falsche Angaben. Bis das Problem auffällt, stehen die schlechten Daten womöglich seit Tagen oder Wochen in der Produktion. dbt Tests verschieben die Qualität nach vorn und fangen Probleme in der Schicht der Transformation ab, bevor sie die nachgelagerten Data Consumer treffen.

Generische Tests: die vier eingebauten Arten

Generische Tests sind parametrisierte Definitionen von Tests, die sich auf jedes Modell und jede Spalte anwenden lassen, indem man sie in einer Beschreibungsdatei in .yml nennt. dbt bringt genau vier eingebaute generische Tests mit:

not_null

Sichert zu, dass eine Spalte keine NULL-Werte enthält. Das ist der grundlegendste Test: Eine Spalte, die immer einen Wert haben soll, darf nicht stillschweigend NULL zulassen.

unique

Sichert zu, dass alle Werte einer Spalte verschieden sind. Wenden Sie das auf jede Spalte eines Primärschlüssels in jedem Modell an, denn doppelte Primärschlüssel führen in den nachgelagerten Aggregationen zu stillem Doppelzählen.

accepted_values

Sichert zu, dass jeder Wert einer Spalte aus einer festgelegten Liste stammt. Nutzen Sie das für Statusfelder, Aufzählungen von Typen und Codes nach ISO.

relationships

Sichert die referenzielle Integrität zu: Jeder Wert einer Spalte existiert in einer bestimmten Spalte eines anderen Modells, also eine Prüfung des Fremdschlüssels. Das fängt verwaiste Datensätze ab, die ein Join sonst still verwerfen würde.

Taxonomie der Testarten in dbt Testarten in dbt GENERISCHE TESTS In schema.yml erklärt · für jedes Modell, jede Spalte not_null Keine NULL-Werte in der Spalte Eingebaut unique Alle Werte sind verschieden Eingebaut accepted_values Wert aus einer festen Liste Eingebaut relationships Referenzielle Integrität (FK-Prüfung) Eingebaut + Pakete dbt_utils, dbt_expectations + eigene Makros im Verzeichnis macros/ SINGULÄRE TESTS Eigene .sql-Dateien im Verzeichnis tests/ -- tests/assert_positive_amount.sql select order_id, amount from {{ ref('stg_orders') }} where amount <= 0 Gibt fehlerhafte Zeilen zurück 0 Zeilen = BESTANDEN Zeilen vorhanden = FEHLER Am besten für komplexe Einzelregeln, die sich nicht verallgemeinern lassen Beide Arten übersetzen zu SQL · Aufruf: dbt test · dbt build
Zum Vergrößern klicken

Singuläre Tests: eigene Zusicherungen in SQL

Für fachliche Regeln, die zu speziell sind, um sie zu verallgemeinern, schreiben Sie einen singulären Test, also eine Datei .sql im Verzeichnis tests/ mit einem SELECT, das die verletzenden Zeilen zurückgibt:

-- tests/assert_orders_amount_positive.sql
-- Every completed order must have a positive amount.
select
    order_id,
    amount
from {{ ref('stg_orders') }}
where status = 'delivered'
  and amount <= 0
-- tests/assert_revenue_matches_order_sum.sql
-- The monthly revenue mart must match the sum of staging orders.
select
    month,
    mart_revenue,
    raw_revenue,
    abs(mart_revenue - raw_revenue) as discrepancy
from (
    select
        date_trunc('month', created_at) as month,
        sum(amount)                     as raw_revenue
    from {{ ref('stg_orders') }}
    where status = 'delivered'
    group by 1
) raw
join (
    select month, revenue as mart_revenue
    from {{ ref('fct_revenue') }}
) mart using (month)
where abs(mart_revenue - raw_revenue) > 0.01

Jede Zeile, die diese SELECT-Anweisungen zurückgeben, ist ein fehlgeschlagener Test. Auch singuläre Tests sind eine Form ausführbarer Dokumentation: Sie machen fachliche Regeln im Code lesbar und prüfbar, statt in Tabellenblättern.

Generische Tests in schema.yml konfigurieren

Generische Tests werden in schema.yml erklärt (oder in jeder anderen Datei .yml im Verzeichnis models/), unter dem Schlüssel tests eines Modells oder einer Spalte:

# models/staging/_staging.yml
version: 2

models:
  - name: stg_orders
    description: "Cleaned order records from the transactional database."
    columns:
      - name: order_id
        description: "Surrogate primary key for each order."
        tests:
          - unique
          - not_null

      - name: status
        description: "Current fulfillment status of the order."
        tests:
          - not_null
          - accepted_values:
              values: ['placed', 'shipped', 'delivered', 'cancelled', 'returned']

      - name: customer_id
        description: "Foreign key to stg_customers."
        tests:
          - not_null
          - relationships:
              to: ref('stg_customers')
              field: customer_id

      - name: amount
        description: "Order total in USD."
        tests:
          - not_null

Fahren Sie alle Tests mit dbt test oder die Tests eines bestimmten Modells mit dbt test --select stg_orders. Jeder Test übersetzt zu einer Abfrage in SQL wie dieser:

-- Compiled not_null test on stg_orders.order_id
select count(*) as failures
from (
    select order_id
    from analytics.staging.stg_orders
    where order_id is null
) dbt_internal_test

Der Schweregrad eines Tests: warn oder error

Von Haus aus beendet ein fehlgeschlagener Test dbt test mit einem Statuscode ungleich null und blockiert damit die Pipelines der CI. Über den Schweregrad lässt sich das abstimmen:

# models/staging/_staging.yml
models:
  - name: stg_orders
    columns:
      - name: amount
        tests:
          - not_null:
              severity: warn          # warn but do not fail the pipeline
          - dbt_utils.expression_is_true:
              expression: ">= 0"
              severity: error         # block pipeline on negative amounts

Nutzen Sie warn für weiche Erwartungen, also Probleme der Datenqualität, von denen Sie wissen wollen, die aber nicht zwingend ein Deployment aufhalten sollen (etwa ein neues, freiwilliges Feld, das noch nicht überall gefüllt ist). Nutzen Sie error (die Voreinstellung) für harte Zusagen, also für Verletzungen, die auf wirklich kaputte Daten hindeuten.

Sie können auch eine Schwelle setzen und den Test erst dann scheitern lassen, wenn der Anteil oder die Zahl der fehlerhaften Zeilen eine Grenze überschreitet. Das ist bei großen Tabellen nützlich, in denen ein kleiner Anteil an NULL-Werten hinnehmbar sein kann:

- name: email
  tests:
    - not_null:
        config:
          warn_if: ">10"
          error_if: ">100"

Pakete aus der Community: dbt_utils und dbt_expectations

Das Ökosystem der Pakete rund um dbt erweitert die eingebaute Bibliothek an Tests erheblich. Zwei Pakete sind weit verbreitet:

dbt_utils

dbt_utils (gepflegt von dbt Labs) liefert Hilfsmittel, darunter generische Tests, die dbt Core nicht mitbringt:

  • expression_is_true: prüft einen beliebigen Ausdruck in SQL, etwa amount > 0 oder end_date >= start_date.
  • not_constant: sichert zu, dass eine Spalte mehr als einen verschiedenen Wert hat (fängt eingefrorene Zulieferungen ab).
  • recency: sichert zu, dass die Daten innerhalb eines festgelegten Zeitfensters frisch sind.
  • equal_rowcount: sichert zu, dass zwei Modelle gleich viele Zeilen haben.
  • cardinality_equality: sichert zu, dass zwei Spalten dieselbe Menge verschiedener Werte haben.
# dbt_utils examples
- name: end_date
  tests:
    - dbt_utils.expression_is_true:
        expression: ">= start_date"

- name: updated_at
  tests:
    - dbt_utils.recency:
        datepart: hour
        field: updated_at
        interval: 25

dbt_expectations

dbt_expectations überträgt das Vokabular von Great Expectations auf dbt und bietet über 50 Testarten, darunter:

  • expect_column_values_to_be_between: sichert zu, dass die Werte in einer Spanne von Zahlen liegen.
  • expect_table_row_count_to_be_between: sichert Grenzen für die Zeilenzahl zu (fängt leere Ladungen ab).
  • expect_column_values_to_match_regex: prüft das Format einer Zeichenkette (E-Mail-Adressen, Codes nach ISO).
  • expect_column_mean_to_be_between: Prüfungen auf die statistische Verteilung.
# dbt_expectations examples
models:
  - name: fct_revenue
    tests:
      - dbt_expectations.expect_table_row_count_to_be_between:
          min_value: 1
          max_value: 5000
    columns:
      - name: revenue
        tests:
          - dbt_expectations.expect_column_values_to_be_between:
              min_value: 0
              max_value: 10000000

Pakete werden installiert, indem man sie in packages.yml erklärt und dbt deps fährt:

# packages.yml
packages:
  - package: dbt-labs/dbt_utils
    version: [">=1.0.0", "<2.0.0"]
  - package: calogica/dbt_expectations
    version: [">=0.10.0", "<1.0.0"]

Eine Strategie für die Testabdeckung

Eine gute Strategie für die Testabdeckung wägt Gründlichkeit gegen Pflegbarkeit ab. Nicht jede Spalte braucht jeden Test: Zu viele Tests erzeugen Rauschen und verlangsamen die Builds, ohne die Qualität im gleichen Maß zu heben.

Immer testen

  • Primärschlüssel: unique und not_null auf dem Schlüssel, der die Granularität jedes Modells bestimmt. Das ist die kleinste noch vertretbare Testabdeckung.
  • Fremdschlüssel: Tests mit relationships auf allen Spalten, über die Modelle verbunden werden.
  • Spalten mit Kategorien: accepted_values auf Statusfeldern, Aufzählungen von Typen und Codes nach ISO.
  • Fachlich verpflichtende Felder: not_null auf Spalten, die nach den Regeln der Domäne niemals leer sein dürfen.

Gezielt testen

  • Stimmigkeit der Aggregate: singuläre Tests, die Zeilenzahlen oder Summen zwischen den Schichten vergleichen (Staging gegen Mart).
  • Aktualität: dbt_utils.recency auf Tabellen mit Ereignissen oder Transaktionen, um veraltete Daten zu finden.
  • Prüfungen auf Spannen: dbt_expectations.expect_column_values_to_be_between für Spalten mit Zahlen, deren Grenzen bekannt sind.

Auslassen oder zurückstellen

  • Felder mit freiem Text, an deren Format keine Erwartung besteht.
  • Spalten, die von der Anlage her wirklich freiwillig sind (dünn besetzte Merkmale).
  • Tests, die einen vollen Scan von Tabellen im Petabyte-Bereich verlangen würden. Ziehen Sie hier --limit oder Makros zum Ziehen von Stichproben in Betracht.

Tests in CI/CD fahren

Der empfohlene Befehl für CI/CD ist dbt build, der Modelle und Tests in der Reihenfolge der Abhängigkeiten fährt. Schlägt ein Test fehl, hört dbt auf, nachgelagerte Modelle zu bauen, und verhindert damit, dass schlechte Daten sich weiter ausbreiten.

# Example GitHub Actions workflow step
- name: dbt build (models + tests)
  run: |
    dbt deps
    dbt build --target ci --select state:modified+
  env:
    DBT_TARGET_SCHEMA: ci_${{ github.run_id }}

Die wichtigsten Praktiken für dbt Tests in CI/CD:

  • Schlanke CI: Nutzen Sie state:modified+, um nur die Modelle und Tests zu fahren, die der aktuelle PR berührt. Das verlangt, die vorige manifest.json aufzubewahren und zu vergleichen.
  • Eigenes Schema für die CI: Fahren Sie die Builds der CI in einem abgetrennten Schema, damit sie weder die Produktion noch die Entwicklung stören.
  • Früh scheitern: Lassen Sie fehlgeschlagene Tests mit dem Schweregrad error den Merge in den Hauptzweig blockieren.
  • Artefakte aufbewahren: Legen Sie run_results.json und manifest.json nach jedem Lauf der CI ab, für den Vergleich im nächsten Lauf und für die Anbindung der Governance.

Die Anbindung an Data Observability

Die Ergebnisse der dbt Tests sind ein erstrangiges Signal für Data Observability, also für die Praxis, Pipelines fortlaufend auf Qualität, Aktualität und Abweichungen im Schema zu überwachen. Plattformen wie Dawiso lesen die Ergebnisse der dbt Tests ein (über run_results.json und das Manifest) und liefern damit aus der Schicht der Governance eine Sicht auf die Datenqualität über die gesamte Schicht der Transformation hinweg.

Im Einzelnen kann Dawiso zeigen:

  • welche Modelle von Tests abgedeckt sind und welche ungetestet bleiben, was ein Risiko für die Governance ist;
  • wie sich bestandene und fehlgeschlagene Tests über die Zeit entwickeln, um Muster der Verlässlichkeit zu erkennen;
  • Werte für die Qualität auf Spaltenebene, hergeleitet aus den Ergebnissen der Tests;
  • Alarme, wenn Tests, die zuvor bestanden wurden, zu scheitern beginnen.

Indem die Metadaten der dbt Tests mit den Einträgen im Datenkatalog von Dawiso verbunden werden, sehen Data Consumer nicht nur, was eine Spalte bedeutet (die Beschreibung aus schema.yml), sondern auch, ob sie gerade geprüft ist (die Testabdeckung) und wann sie ihre Prüfungen zuletzt bestanden hat, alles ohne die Oberfläche des Katalogs zu verlassen. Das schließt die Lücke zwischen dem Arbeitsablauf der dbt Tests im Engineering und dem Bedarf der Governance an Nachweisen, dass die Datenbestände die Ansprüche an die Qualität erfüllen, bevor geschäftskritische Prozesse sie nutzen.

Wie sich die Ergebnisse der Tests in das größere Bild aus Lineage und Metadaten fügen, steht im Leitfaden zur Lineage in dbt.

Häufige Antimuster beim Testen, die zu vermeiden sind

  • Testen, ohne zu handeln: Tests, die immer warnen und nie untersucht werden, erzeugen Ermüdung gegenüber Alarmen. Jede Warnung braucht einen Owner und einen Plan zur Behebung.
  • Primärschlüssel nicht testen: unique + not_null auf den Schlüsseln der Granularität auszulassen ist das häufigste Versäumnis, denn doppelte Schlüssel führen in den nachgelagerten Aggregationen zu stillem Doppelzählen.
  • Erlaubte Werte fest eintragen, ohne sie mit der Quelle abzugleichen: Listen in accepted_values, die nicht mit den Aufzählungen des Quellsystems im Gleichstand gehalten werden, veralten. Laden Sie Referenzdaten lieber als Seeds und nutzen Sie stattdessen einen Test mit relationships.
  • In jedem Lauf der CI alle Tests fahren: In großen Projekten ist es zu langsam, bei jedem Commit alle Tests zu fahren. Nutzen Sie eine schlanke CI mit state:modified+ und fahren Sie die vollständige Testreihe nach Zeitplan, etwa nachts.
  • Tests auf Staging-Modellen auslassen: Probleme, die in der Schicht des Staging auffallen, sind billiger zu beheben als Probleme, die erst in der Mart-Schicht auffallen. Ergänzen Sie auf Staging-Modellen immer Tests auf den Primärschlüssel und auf not_null.

In Aktion erleben

Data & Analytics Catalog

Schaffen Sie eine einheitliche Sicht auf Ihre Datenbestände und gewinnen Sie mit automatischer Datenerkennung schneller Erkenntnisse.

Ein Cookie am Tag hält schlechte UX fern.

Wir verwenden Cookies, um Inhalte und Werbung zu personalisieren und unseren Traffic zu analysieren. Außerdem geben wir Informationen zu Ihrer Nutzung unserer Website an unsere Partner für Werbung und Analyse weiter, die sie mit weiteren Daten zusammenführen können, die Sie ihnen bereitgestellt haben oder die sie bei Ihrer Nutzung ihrer Dienste gesammelt haben. Mit einem Klick auf „Alle akzeptieren“ erlauben Sie uns, Cookies für Analyse und Werbung über Google Tag Manager zu verwenden. Sie können Cookies auch einzeln einstellen.

Einstellungen zur Zustimmung anpassen

Wir verwenden Cookies, um Inhalte und Werbung zu personalisieren und unseren Traffic zu analysieren. Außerdem geben wir Informationen zu Ihrer Nutzung unserer Website an unsere Partner für Werbung und Analyse weiter. Datenschutzerklärung

Notwendige Cookies ermöglichen zentrale Funktionen der Website, etwa die Anmeldung und die Kontoverwaltung. Ohne unbedingt notwendige Cookies lässt sich die Website nicht richtig nutzen.

Funktionale Cookies speichern Informationen zu Besuchern der Website, etwa Sprache, Zeitzone oder erweiterte Inhalte.

Analyse-Cookies zeigen, wie Besucher die Website nutzen. Sie können nicht dazu verwendet werden, einen bestimmten Besucher direkt zu identifizieren.

Wir verwenden Microsoft Clarity, um zu sehen, wie Sie unsere Website nutzen (einschließlich Heatmaps und Sitzungsaufzeichnungen), damit wir sie verbessern können. Mit der Nutzung unserer Website stimmen Sie zu, dass wir und Microsoft diese Daten erheben und verwenden. Details finden Sie in unserer Datenschutzerklärung.

Werbe-Cookies erkennen Besucher über verschiedene Websites hinweg, etwa bei Content-Partnern oder Bannernetzwerken. Unternehmen können damit ein Profil der Interessen eines Besuchers aufbauen oder auf anderen Websites passende Werbung anzeigen.