Inferensys

Glossary

dbt Test

A dbt test is a declarative data quality check defined in SQL or YAML within the dbt (data build tool) framework to validate assumptions about data integrity and business logic.
Developer demonstrating multi-agent tool use, agent tool selection interface on laptop, casual tech demo moment.
AUTOMATED DATA TESTING

What is a dbt Test?

A dbt test is a declarative data quality check, defined within the dbt (data build tool) framework, that validates assumptions about the structure and content of data in tables and models.

A dbt test is a declarative data quality assertion, written in SQL or defined in YAML configuration, that validates assumptions about tables and models within a dbt project. It is a core component of the data build tool framework, enabling test-driven data development. Tests are executed via the dbt test command and check for conditions like column uniqueness, non-null values, referential integrity between models, and adherence to custom business logic. Results are logged, providing a clear audit trail of data health.

Tests are categorized as generic (pre-built for common constraints like unique and not_null) or singular (custom SQL assertions). They are defined in .yml files alongside model definitions, promoting data quality as code. When integrated into a CI/CD pipeline, failing tests can act as pipeline-gated checks, preventing flawed data from propagating. This embeds validation directly into the transformation layer, shifting quality left and making data reliability a shared, automated engineering responsibility.

AUTOMATED DATA TESTING

Core Characteristics of dbt Tests

dbt tests are declarative data quality checks defined as code within the dbt framework. They validate assumptions about data in tables and models, such as uniqueness and referential integrity, and are executed as part of the data build process.

01

Declarative & Configuration-Driven

dbt tests are defined declaratively using YAML configuration files or simple SQL SELECT statements. Instead of writing procedural code to check data, you specify what condition the data must satisfy (e.g., not_null, unique). The dbt test execution engine interprets this configuration and runs the appropriate validation query. This approach separates the test logic from the execution engine, making tests portable, easy to version control, and less error-prone than custom scripting.

  • Example: A YAML block defining a not_null test on a user_id column.
  • Key Benefit: Enforces data quality as code, allowing tests to be reviewed, shared, and automated alongside transformation logic.
02

Built-in Generic & Custom Singular Tests

dbt provides two primary test types. Generic tests (like unique, not_null, accepted_values, relationships) are reusable validations applied to columns via YAML configuration. Singular tests are one-off, custom SQL queries that return failing rows; they are ideal for validating complex business logic that generic tests cannot cover.

  • Generic Test Example: relationships test ensures referential integrity between a orders.customer_id column and a customers.id column.
  • Singular Test Example: A SQL file that checks if a derived profit_margin column is always below 100%.
  • Separation of Concerns: This duality allows teams to standardize common checks while retaining flexibility for domain-specific validation.
03

Integrated into the dbt DAG

Tests are nodes within dbt's Directed Acyclic Graph (DAG) of data transformations. When you run dbt test, the framework understands the dependencies between your models and tests, ensuring tests run on the correct, materialized data. Tests can be executed on specific models, directories, or tags, providing granular control.

  • Pipeline-Gated Execution: Tests can be configured to run immediately after a model is built, acting as a quality gate that fails the pipeline if data assumptions are violated, preventing bad data from propagating downstream.
  • Dependency-Aware: The test for a staging model will run before the test for a downstream mart model that depends on it, ensuring validation occurs in the correct order.
04

SQL-Based & Warehouse-Native

All dbt tests are compiled into standard SQL SELECT statements that are executed directly in your data warehouse (e.g., Snowflake, BigQuery, Redshift). The test "passes" if the query returns zero rows; any returned rows are considered failures. This warehouse-native approach leverages the power and scalability of modern cloud platforms.

  • Performance: Testing happens where the data lives, eliminating costly data movement.
  • Transparency: The generated SQL is inspectable, making it easy to debug why a test failed by examining the failing rows.
  • Consistency: Uses the same connection and compute credentials as your dbt models, ensuring a unified environment.
05

Results as Data & Operational Integration

dbt writes test results to a table in your warehouse (typically dbt_test__audit), turning validation outcomes into queryable data. This enables building custom dashboards, tracking test failure rates over time, and setting up alerts. The results structure includes the test name, status, execution time, and the specific failing rows.

  • Observability: Facilitates integration with broader data observability platforms by exposing test results via a clean data interface.
  • Historical Analysis: Teams can analyze trends, such as which models or tests fail most frequently, to prioritize data quality investments.
  • Alerting: Can be integrated with tools like Slack or PagerDuty by querying the results table after a run.
06

Complement to Broader Data Quality Frameworks

While dbt tests are excellent for validating the output of transformations defined within dbt, they are often part of a larger data quality posture. They typically serve as unit and integration tests for the dbt-managed layer of the data stack.

  • Scope: Best for testing business logic and structural integrity after transformation. They are less suited for monitoring data drift in source systems or validating raw, untransformed data at ingestion.
  • Synergy: Often used alongside specialized tools like Great Expectations or Soda Core for profiling, monitoring, and testing non-dbt pipelines or raw data sources. dbt tests act as the quality gate for the "trusted" data layer.
AUTOMATED DATA TESTING

How dbt Tests Work: Mechanism and Execution

A dbt test is a declarative data quality check, defined in SQL or YAML, that validates assumptions about tables and models within the dbt (data build tool) framework.

A dbt test is executed as a SQL query that returns failing rows. The core mechanism is declarative: you define the expected condition (e.g., unique, not_null, relationships), and dbt's test execution engine compiles it into a SELECT statement that identifies violations. For a uniqueness test, this query selects duplicate values; for a referential integrity test, it finds orphaned foreign keys. Tests run within your data warehouse, leveraging its compute, and results are logged for review.

Execution is managed via the dbt test command, which runs tests based on selection criteria (e.g., on a specific model). Tests can be configured as pipeline-gated checks that fail the run or as warnings. Results are aggregated into a summary, showing pass/fail status and the failing rows. This integrates with data observability platforms by exposing results, enabling continuous testing and monitoring of data quality gates within the broader data reliability engineering practice.

FEATURE COMPARISON

dbt Test vs. Other Data Testing Approaches

A comparison of dbt Test's declarative, SQL-centric testing model against other common frameworks and methodologies for automated data quality validation.

Feature / Characteristicdbt TestProgrammatic Assertions (e.g., Python)External Data Quality Platforms (e.g., Great Expectations, Soda)

Primary Paradigm

Declarative (YAML/SQL)

Imperative (Code)

Declarative (Configuration/Code)

Tightness of Integration with Transformation Logic

Native Execution within Data Pipeline

Varies (API-based)

Test Definition Location

Alongside models in project

Separate scripts or pipeline code

External configuration files or UI

Primary Test Author

Analytics/Data Engineer

Data/Software Engineer

Data Engineer, Data Steward

Test Orchestration & Scheduling

Via dbt Cloud/dbt Core

Custom (Airflow, Prefect, etc.)

Native scheduler or external orchestrator

Built-in Test Types

Generic (unique, not_null, etc.) & Singular (custom SQL)

Fully custom

Extensive library (freshness, distribution, regex, etc.)

Result Storage & History

Within run artifacts (logs, JSON); requires external tool for history

Custom implementation required

Native centralized database & UI

Alerting & Incident Management

Basic (run failures); requires integration

Fully custom implementation

Native integrations (Slack, PagerDuty, etc.)

Data Profiling & Discovery

Limited (via dbt test --store-failures)

None (must be built)

Core feature

Data Lineage Integration

Native (tests are nodes in dbt DAG)

None

Varies (often separate from transformation lineage)

Learning Curve for Basic Use

Low

High

Medium

Cost for Core Functionality

Open-source (dbt Core)

Open-source (custom code)

Freemium or commercial license

Ideal Use Case

Testing transformations within a dbt project

Low-level validation in custom Python pipelines

Organization-wide quality monitoring across heterogeneous sources

AUTOMATED DATA TESTING

Common dbt Test Examples and Use Cases

dbt tests are declarative SQL or YAML statements that validate data integrity and business logic. This section details the most common test types and their practical applications in data pipelines.

01

Built-in Generic Tests

dbt provides four core, pre-built tests that validate fundamental data properties without custom SQL. These are the most commonly used tests for basic data hygiene.

  • unique: Ensures all values in a specified column (or combination of columns) are distinct. Essential for primary keys.
  • not_null: Verifies that a column contains no NULL values, enforcing completeness for critical fields.
  • accepted_values: Confirms that all data in a column matches a predefined list of allowed values (e.g., status IN ('active', 'inactive', 'pending')).
  • relationships (Referential Integrity): Validates that every value in a child column exists in a parent column, enforcing foreign key constraints.

Example YAML definition:

yaml
models:
  - name: orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
      - name: customer_id
        tests:
          - relationships:
              to: ref('customers')
              field: customer_id
02

Custom Singular Tests

Singular tests are bespoke SQL queries where a result set indicates a test failure. They are used for complex, one-off validation logic that generic tests cannot cover.

  • Structure: A .sql file in the tests/ directory containing a SELECT statement. Any rows returned by the query are interpreted as failing records.
  • Use Cases:
    • Validating multi-column business logic (e.g., discount_amount <= order_total).
    • Checking for orphaned records across multiple tables.
    • Enforcing sophisticated date logic (e.g., ship_date >= order_date).
    • Identifying statistical outliers beyond simple thresholds.

Example (tests/ship_date_after_order_date.sql):

sql
SELECT order_id, order_date, ship_date
FROM {{ ref('orders') }}
WHERE ship_date < order_date
-- If this returns rows, the test fails.
03

Custom Generic Tests

Custom generic tests are parameterized SQL macros that can be reused across multiple columns or models, promoting consistency and reducing code duplication.

  • Structure: Defined as a Jinja macro in a .sql file within the macros/ directory. They accept arguments like column_name and model.
  • Use Cases:
    • Financial Data: Ensuring debits and credits sum to zero for a ledger.
    • ID Format Validation: Checking that a column matches a specific regex pattern (e.g., UUID, email).
    • Freshness Thresholds: Testing if a timestamp column is within an acceptable range of the current time.
    • Percentage Bounds: Verifying a column's value is between 0 and 100.

Example macro (macros/test_value_between.sql):

sql
{% test value_between(model, column_name, min_value, max_value) %}
SELECT *
FROM {{ model }}
WHERE {{ column_name }} < {{ min_value }}
   OR {{ column_name }} > {{ max_value }}
{% endtest %}

Usage in YAML:

yaml
columns:
  - name: satisfaction_score
    tests:
      - value_between:
          min_value: 0
          max_value: 10
04

Schema & Data Type Tests

These tests validate the structure and formatting of data, ensuring it aligns with expected contracts and downstream consumption needs.

  • Schema Tests: While not a native dbt test type, schema validation is achieved via dbt build or dbt run coupled with tests. The compilation and execution of models inherently validates SQL syntax and references. Formal schema contracts are often enforced via pipeline-gated tests on column existence and data type.
  • Data Type Validation: Implemented using custom generic tests or singular tests.
    • Checking that a numeric column contains only integers.
    • Ensuring a VARCHAR column does not exceed a defined maximum length.
    • Verifying a DATE column has no invalid or out-of-range dates.
  • Integration with Data Contracts: These tests operationalize the guarantees of a data contract, acting as the enforceable validation layer for schema stability and type safety.
05

Business Logic & Integrity Tests

This category encompasses the most valuable tests, which encode critical domain knowledge and ensure data accurately reflects real-world rules.

  • Aggregate Logic: Testing that summed totals in summary tables match granular transaction tables.
  • Derived Column Accuracy: Validating that a calculated column (e.g., revenue = quantity * unit_price) is computed correctly.
  • Hierarchical Integrity: Ensuring roll-up relationships are maintained (e.g., all city-level records map to a valid country).
  • Uniqueness Across Sources: Confirming there is no overlap or duplication of key identifiers when data is merged from multiple systems.
  • Temporal Consistency: Checking that slowly changing dimensions (SCD) are applied correctly and history is preserved.

These tests move beyond basic hygiene to protect the business correctness of data products. They are often the most complex to write but provide the highest return on investment by preventing costly decision-making errors.

06

Test Execution & Orchestration

Understanding how and when tests run is crucial for integrating them into a Continuous Testing paradigm.

  • Command-Line Execution:
    • dbt test: Runs all tests on all models in the project.
    • dbt test --select test_type:singular: Runs only singular tests.
    • dbt test --select model_name+: Runs tests for a specific model and all its downstream dependencies.
  • Pipeline Integration:
    • As a Quality Gate: Tests are executed after dbt run in a CI/CD pipeline. A single test failure can be configured to fail the entire job, blocking the promotion of bad data.
    • In Production: A subset of non-destructive smoke tests or canary tests can be run on production data to monitor health.
  • Test Severity: Tests can be configured as ERROR (fails the run) or WARN (logs a warning but allows the run to proceed).
  • Result Handling: Results are logged to the command line and stored in the data warehouse, enabling aggregation in dashboards for test coverage and trend analysis.
DBT TEST

Frequently Asked Questions

A dbt test is a declarative data quality check, defined in SQL or YAML within the dbt (data build tool) framework, that validates assumptions about data in tables and models. This FAQ addresses common questions about its implementation, mechanics, and role in automated data testing.

A dbt test is a declarative data quality assertion, defined as SQL or YAML configuration within the dbt framework, that validates assumptions about data in tables and models. It works by executing a SQL query that returns failing rows; an empty result set indicates a pass, while any returned rows indicate a failure. Tests are defined in a schema.yml file or as generic test files and are executed via the dbt test command, which compiles the test logic, runs it against the target data warehouse, and reports results. Core mechanics include:

  • Declarative Syntax: Tests specify what condition the data must satisfy (e.g., unique, not_null) rather than how to check it.
  • Query-Based Validation: Each test is transpiled into a SELECT statement that identifies violating records.
  • Integrated Execution: Tests run within the same environment and connection as dbt models, ensuring consistent context.
  • Result Aggregation: Outcomes are summarized in the CLI and can be logged to a run_results.json file for integration with data observability platforms.
Prasad Kumkar

About the author

Prasad Kumkar

CEO & MD, Inference Systems

Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.

His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.