Back to blog

DBT Contracts vs Data Tests on Snowflake: Ensuring Data Quality and Reliability

The views I express here are mine alone and do not necessarily reflect the opinions or official position of my employer.

Data quality and integrity is very important in the field of data engineering. When working with DBT (Data Build Tool) on Snowflake, both contracts and data tests are essential for ensuring data quality and reliability. However, they serve different purposes and are implemented differently and there are several caveats that needs to be picked up.

DBT Contracts

DBT Contracts are a feature that allows you to enforce the schema (columns, data types, and nullability) of your models. When a contract is enabled, DBT will validate that the model's output matches the declared schema before materializing the table or view in Snowflake.

Key Features

  • Schema enforcement: Ensures columns, data types, and nullability match the contract.
  • Early error detection: Fails the build if the schema does not match, preventing bad data from being loaded.
  • Documentation: Contracts serve as living documentation for your model's expected output.

Example

models:
  - name: customers
    config:
      contract:
        enforced: true
    columns:
      - name: id
        data_type: integer
        constraints:
          not_null: true
      - name: email
        data_type: string
        constraints:
          not_null: true

When to Use

  • When you want to guarantee the structure of your data models.
  • When downstream systems depend on a fixed schema.

DBT Data Tests

DBT Data Tests are SQL queries that check the validity of your data. They are used to assert business logic or data quality rules, such as uniqueness, referential integrity, or value ranges.

Key Features

  • Custom logic: Write tests for any business rule using SQL.
  • Row-level validation: Identify specific records that violate rules.
  • Test types: Built-in (e.g., unique, not_null) and custom tests.

Example

-- tests/not_null_email.sql
SELECT *
FROM {{ ref('customers') }}
WHERE email IS NULL
models:
  - name: customers
    data_tests:
      - not_null:
          column_name: email
      - unique:
          column_name: id

When to Use

  • To validate data quality and business rules.
  • To catch data issues that schema contracts cannot enforce (e.g., value ranges, referential integrity), and because Snowflake constraints are only applied at DDL time and do not guarantee underlying data quality, therefore for ongoing validation you should add explicit data tests (e.g., dbt generic/singular tests) in your pipeline.

Comparison Table

FeatureDBT ContractDBT Data Test
PurposeSchema enforcementData quality validation
Enforced byDBT at build timeDBT at test time
Error TypeBuild fails on mismatchTest fails, build can pass
Use CaseStructure & type guaranteeBusiness logic & data rules
ExampleData types, nullabilityUniqueness, value ranges

Summary

  • Use contracts to enforce the structure and data_type of your models in Snowflake.
  • Use data tests to validate the quality and correctness of your data.
  • Both are complementary and should be used together for robust data pipelines.

Airflow Example: Where Contracts and Data Tests Stop

Consider an Airflow DAG that orchestrates your DBT workflow with two main tasks: dbt run and dbt test.

  • DBT Contract: If a contract is violated (e.g., the model output doesn't match the declared schema), the failure occurs during the dbt run step. Airflow will mark the dbt run task as failed, and downstream tasks (including dbt test) will not execute.
  • DBT Data Test: If the contract passes but a data test fails (e.g., a uniqueness test fails), the failure happens during the dbt test step. Airflow will mark the dbt test task as failed, but the dbt run task will have succeeded.

Summary:

  • Contracts stop the pipeline at the build/run phase if the schema is incorrect.
  • Data tests stop the pipeline at the test phase if data quality rules are violated.

This separation allows you to catch structural issues early and business logic issues after data is loaded.