Data Engineering

Data Testing Frameworks: dbt, Great Expectations, Soda, pytest

Praktischer Vergleich der vier Data-Testing-Frameworks — dbt-Tests, Great Expectations, Soda Core, pytest — mit Code-Beispielen und Auswahlhilfe.

Harbinger Team14. Mai 20267 Min. LesezeitAktualisiert 14.5.2026
  • data testing
  • dbt
  • great expectations
  • soda
  • pytest
  • data quality
  • data engineering
Inhaltsverzeichnis17 Abschnitte

TL;DR: dbt-Tests für SQL-Model-Output, pytest für Python-Transform-Logik, Soda Core für Pipeline-Gates (Freshness, Cross-Tabellen), Great Expectations für Profiling und Quality-Reports. Kein Tool deckt alle vier Layer ab — versuch das nicht.

Eine schlechte Zeile in einer Faktentabelle kostet mehr als die Engineering-Stunden, sie abzufangen. Sie kostet das Vertrauen jedes Analysten, der darauf einen Report gebaut hat.

Data-Testing-Frameworks existieren, um Datenqualitätsprobleme zu fangen, bevor sie Downstream-Konsumenten erreichen. Vier Tools dominieren den Bereich: dbt-Tests, Great Expectations, Soda Core und pytest. Sie überlappen sich im Zweck, unterscheiden sich aber deutlich in Philosophie, Integrationsfläche und operativer Komplexität.

Schnellvergleich

dbt-TestsGreat ExpectationsSoda Corepytest
HauptzweckSQL-Model-ValidierungDataset-Profiling & ExpectationsPipeline-Level-Quality-ChecksUnit-Tests von Python-Logik
KonfigurationYAMLPython / JSONYAML (SodaCL)Python
Läuft indbt-PipelineStandalone oder AirflowStandalone oder AirflowCI/CD, lokale Dev
LernkurveNiedrigHochMittelNiedrig (mit Python-Kenntnissen)
Reporting-UIdbt docsData Docs (self-hosted)Soda Cloud (SaaS)Console / custom
Best fitdbt-native TeamsKomplexe Profiling-NeedsCross-Platform-Quality-GatesTransformation-Code testen
Open SourceJaJaJa (Core)Ja

dbt-Tests

dbt hat ein natives Testing-Framework in die CLI eingebaut. Tests werden in .yml-Files neben deinen Models definiert und mit dbt test ausgeführt.

Eingebaute generic Tests

# models/schema.yml
models:
  - name: dim_customer
    columns:
      - name: customer_sk
        tests:
          - unique
          - not_null
      - name: country_code
        tests:
          - not_null
          - accepted_values:
              values: ['DE', 'FR', 'US', 'GB', 'NL']
      - name: customer_id
        tests:
          - relationships:
              to: ref('stg_customers')
              field: customer_id

Diese vier eingebauten Tests (unique, not_null, accepted_values, relationships) decken den Großteil dessen ab, was die meisten Teams brauchen.

Custom Singular Tests

Für Logik, die nicht in Generic Tests passt, schreib einen Singular Test — eine SQL-Query, die nur dann Zeilen zurückgibt, wenn etwas falsch ist:

-- tests/assert_no_negative_revenue.sql
-- Fails (returns rows) if any order has negative revenue
SELECT
    order_id,
    revenue
FROM {{ ref('fct_orders') }}
WHERE revenue < 0

Gibt diese Query Zeilen zurück, markiert dbt test sie als failed. Einfach, versioniert und neben dem Model, das er schützt.

Wann dbt-Tests nutzen: Du nutzt schon dbt. Tests sind die richtige erste Verteidigungslinie für SQL-Model-Output-Validierung. Starte hier.


Great Expectations

Great Expectations (GX) ist die mächtigste Option, aber auch die komplexeste im Setup. Das Kernkonzept ist eine Expectation Suite — eine Sammlung von Assertions über ein Dataset, die du einmal definierst und wiederholt laufen lässt.

# Python: Great Expectations core v1 (simplified)
import great_expectations as gx

context = gx.get_context()

# Connect to a data source
datasource = context.sources.add_pandas_filesystem(
    name="my_data",
    base_directory="./data"
)

asset = datasource.add_csv_asset(name="orders", batching_regex=r"orders_(?P<date>\d{8})\.csv")
batch_request = asset.build_batch_request()

# Define expectations
validator = context.get_validator(batch_request=batch_request)
validator.expect_column_values_to_not_be_null("order_id")
validator.expect_column_values_to_be_between("amount", min_value=0, max_value=100000)
validator.expect_column_values_to_be_in_set("status", ["pending", "completed", "refunded"])
validator.expect_column_to_exist("customer_id")

# Save suite and run checkpoint
validator.save_expectation_suite()
results = context.run_checkpoint(checkpoint_name="orders_checkpoint")
print(results["success"])  # True / False

Great Expectations generiert Data Docs — menschenlesbare HTML-Reports, die zeigen, welche Expectations passed, welche failed, und Daten-Profile pro Spalte. Das ist der größte Differentiator.

Stärken:

  • Spalten-Profiling und Verteilungs-Analyse
  • Data Docs für stakeholder-taugliche Quality-Reports
  • Funktioniert mit Pandas, Spark, SQL-Datenbanken

Schwächen:

  • Konfiguration ist verbose und komplex
  • Onboarding dauert Stunden, nicht Minuten
  • Expectation-Suites pflegen, während sich Daten entwickeln, braucht Disziplin

Wann Great Expectations nutzen: Du brauchst Daten-Profiling neben Validierung, baust einen Data-Quality-Reporting-Layer für Nicht-Engineers, oder arbeitest außerhalb von dbt.


Soda Core

Soda nutzt eine zweckgebaute YAML-DSL namens SodaCL (Soda Checks Language). Liest sich wie Klartext und integriert sich sauber in Airflow, GitHub Actions oder jeden Orchestrator.

# checks/orders_checks.yml (SodaCL)
checks for orders:
  - row_count > 0
  - missing_count(order_id) = 0
  - duplicate_count(order_id) = 0
  - invalid_percent(status) < 1%:
      valid values: [pending, completed, refunded, cancelled]
  - avg(amount) between 10 and 5000
  - freshness(order_date) < 24h

Mit der CLI ausgeführt:

# Bash: run Soda checks against a PostgreSQL table
soda scan -d my_postgres_connection -c configuration.yml checks/orders_checks.yml

Der freshness-Check ist ein Highlight: er validiert, dass Daten kürzlich aktualisiert wurden — etwas, das dbt-Tests nicht nativ ausdrücken können.

Stärken:

  • Lesbares YAML ist auch für Analysten und Data Owner zugänglich, nicht nur Engineers
  • Freshness-Checks eingebaut
  • Saubere Orchestrator-Integration als Pipeline-Step
  • Soda Cloud (SaaS) bietet Alerting und historisches Tracking

Schwächen:

  • Komplexe Conditional-Logic ist in YAML unbequem
  • Soda Cloud ist ein bezahltes Produkt (Soda Core ist kostenlos)
  • Weniger reifes Ökosystem als Great Expectations

Wann Soda Core nutzen: Du willst Quality-Checks, die auch Nicht-Engineers lesen und ändern können. Funktioniert besonders gut als Pipeline-Gate in Airflow oder Prefect.


pytest für Datenpipelines

pytest ist ein General-Purpose-Python-Testing-Framework. Nicht datenspezifisch, aber unverzichtbar zum Testen des Python-Codes um deine Daten herum — Transformationsfunktionen, Schema-Mappings, Custom-Logik.

# Python (pytest): test a transformation function
import pytest
import pandas as pd
from my_pipeline.transforms import normalize_country_code, compute_revenue_bucket

def test_normalize_country_code_uppercase():
    assert normalize_country_code("de") == "DE"
    assert normalize_country_code("fr") == "FR"

def test_normalize_country_code_invalid_raises():
    with pytest.raises(ValueError, match="Invalid country code"):
        normalize_country_code("XX")

def test_compute_revenue_bucket():
    df = pd.DataFrame({"amount": [5, 50, 500, 5000]})
    result = compute_revenue_bucket(df)
    assert list(result["bucket"]) == ["micro", "small", "medium", "large"]

def test_compute_revenue_bucket_empty_input():
    df = pd.DataFrame({"amount": []})
    result = compute_revenue_bucket(df)
    assert len(result) == 0

pytest ist auch ausgezeichnet für Snapshot-Tests auf kleinen Referenz-Datasets — Fixture laden, Transformation laufen lassen, prüfen, dass der Output dem erwarteten Ergebnis entspricht:

# Python (pytest): snapshot test with fixture
import pandas as pd

def test_deduplication_logic(tmp_path):
    input_data = pd.DataFrame({
        "id": [1, 1, 2],
        "value": ["a", "a_duplicate", "b"]
    })
    expected = pd.DataFrame({
        "id": [1, 2],
        "value": ["a", "b"]
    })
    result = deduplicate(input_data, key="id", keep="first")
    pd.testing.assert_frame_equal(result.reset_index(drop=True), expected)

Wann pytest nutzen: Du schreibst Python-ETL-Code, Custom-Spark-Transformations oder Utility-Funktionen. Jede Funktion, die Daten transformiert, sollte einen Unit-Test haben.


Layern: alle vier zusammen

Diese Tools schließen sich nicht aus. Ein reifes Data-Team nutzt typischerweise alle vier auf verschiedenen Layern:

Layer 1 — Unit-Tests:        pytest (testet Python-Transform-Logik)
Layer 2 — Model-Validation:  dbt-Tests (testet SQL-Model-Output)
Layer 3 — Pipeline-Gates:    Soda Core (Freshness, Volume, Cross-Tabellen-Checks)
Layer 4 — Profiling/Audits:  Great Expectations (Onboarding neuer Quellen, Quality-Reports)

Zu versuchen, nur ein Tool für alle vier Layer zu nutzen, ist der Punkt, an dem Teams in Schwierigkeiten geraten. dbt-Tests sind super auf Layer 2, können aber Layer 1 nicht oder Freshness ausdrücken. pytest ist nutzlos auf Layer 2. Wähl das richtige Tool pro Layer.


Häufige Pitfalls

  • Das Warehouse testen statt das Model: dbt-Tests laufen gegen materialisierte Tabellen. Wenn deine Quelldaten schon korrupt sind, bedeutet bestandene dbt-Tests nur, dass das Model die schlechten Daten getreu reproduziert hat. Ergänze Source-Tests.
  • Kein Test für Volume: Eine stille Truncation (0 Zeilen geladen) besteht die meisten Tests. Immer einen row_count > 0-Check ergänzen.
  • Über-Testen: Schreib nicht not_null-Tests für jede Spalte in jedem Model. Konzentrier dich auf Spalten, die Downstream-Failures verursachen würden, wenn sie null wären.
  • Expectations, die nie aktualisiert werden: Eine Great-Expectations-Suite, die auf 6 Monaten Daten erstellt wurde, fällt um, wenn du ein neues Land ergänzt. Wartung ist der versteckte Kostenpunkt.

FAQ

Wenn ich nur ein Tool wählen darf — welches?

Bist du dbt-native: dbt-Tests. Bist du nicht auf dbt und brauchst etwas, das Nicht-Engineers lesen können: Soda Core. Bist du voll Python-zentriert: pytest + Great Expectations.

Kann ich dbt-Tests und Soda parallel betreiben?

Ja, üblicher Setup: dbt-Tests im dbt-Lauf, Soda als separater Pipeline-Step für Freshness und Cross-Tabellen-Checks. Doppelt geprüft ist nicht doppelt teuer — die Failures fangen unterschiedliche Klassen ab.

Wie verhindere ich, dass Tests zur Wartungslast werden?

Tests in Severity-Tiers staffeln (P0/P1/P2), nicht jedes Feld testen, sondern die mit echter Downstream-Auswirkung. Tests, die seit 6 Monaten nicht gefailt sind, sind Kandidaten zum Streichen oder Lockern.

Sind Data-Quality-Tools DSGVO-relevant?

Indirekt: gute Quality-Tests fangen Datenleckagen frühzeitig ab (z. B. eine Spalte, die plötzlich PII enthält), helfen bei Auskunftsrecht-Anfragen (Konsistenz-Checks) und sichern Retention-Pipelines.

Reicht Soda Cloud oder brauche ich GX?

Soda Cloud deckt 80 % der typischen Quality-Reporting-Needs. Wenn du echtes Profiling und Verteilungs-Analyse für unbekannte Datenquellen brauchst, ist GX immer noch das richtige Tool.


Wrap-up

Für die meisten dbt-nativen Teams: mit dbt-Tests starten, Soda Core für Freshness und Cross-Tabellen-Checks ergänzen. Great Expectations dazuholen, wenn du Daten-Profiling oder Quality-Reporting für nicht-technische Stakeholder brauchst. pytest immer dann, wenn deine Pipeline Python-Logik enthält, die testenswert ist.

Data Testing ist kein einzelnes Tool — es ist eine Disziplin, die über Layer hinweg angewandt wird. Teams, die das gut machen, behandeln Test-Failures wie CI/CD-Failures: nichts geht raus, bevor sie grün sind.


Weiterlesen


Stand: 14. Mai 2026.

H

Geschrieben von

Harbinger Team

Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastruktur­kritische Tech-Entscheidungen — keine Marketing- Folien, sondern echte Trade-offs aus Production-Workloads.

Hat dir das geholfen?

Jede Woche ein neuer Artikel über DACH-Cloud, Data und AI — direkt in dein Postfach. Kein Spam, kein Marketing-Sprech.

Kein Spam. 1-Klick-Abmeldung. Datenschutz bei Loops.so.