dbt Testing Guide
Testing is fundamental to data reliability. This guide covers everything you need to implement comprehensive data quality checks in your dbt project using dbt-Workbench.
Why test your data?
Data teams face quality challenges that testing addresses:
Catch issues early
Tests identify data problems before they reach downstream consumers. A failed uniqueness test on a primary key prevents bad joins and incorrect aggregations.
Build trust
Stakeholders trust data products when they know quality checks are in place. Testing documentation serves as a contract about data expectations.
Enable safe changes
Tests provide safety nets when refactoring models. If tests pass after changes, you have confidence nothing broke.
Satisfy compliance
Many regulations require data quality validation. Tests provide audit trails proving data meets standards.
Built-in dbt tests
dbt includes four built-in tests covering common data quality scenarios:
unique
Ensures column values are unique, essential for primary keys and natural keys.
models:
- name: customers
columns:
- name: customer_id
tests:
- unique
not_null
Verifies columns contain no null values, critical for required fields.
models:
- name: orders
columns:
- name: order_id
tests:
- not_null
- name: customer_id
tests:
- not_null
accepted_values
Validates column values match an allowed set, useful for enums and status fields.
models:
- name: orders
columns:
- name: status
tests:
- accepted_values:
values: ['pending', 'shipped', 'delivered', 'cancelled']
relationships
Checks referential integrity between models, preventing orphaned records.
models:
- name: orders
columns:
- name: customer_id
tests:
- relationships:
to: ref('customers')
field: customer_id
Configuring tests in schema.yml
Tests are defined in schema.yml files alongside your models:
version: 2
models:
- name: stg_customers
description: Staged customer data from raw source
columns:
- name: customer_id
description: Primary key for customers
tests:
- unique
- not_null
- name: email
description: Customer email address
tests:
- not_null
- unique
- name: customer_segment
description: Customer classification
tests:
- accepted_values:
values: ['enterprise', 'mid_market', 'smb']
config:
severity: warn
Test configuration options
Severity levels
Control whether test failures stop the pipeline or just warn:
tests:
- unique:
config:
severity: error # Default - stops execution
- accepted_values:
config:
severity: warn # Logs issue but continues
Where clauses
Test subsets of data with where conditions:
tests:
- not_null:
config:
where: "created_at > '2024-01-01'"
Store failures
Save failing records for investigation:
tests:
- unique:
config:
store_failures: true
Custom generic tests
Build reusable tests for business-specific logic:
Creating a custom test
Create macros/test_positive_value.sql:
{% test positive_value(model, column_name) %}
select *
from {{ model }}
where {{ column_name }} <= 0
{% endtest %}
Using custom tests
models:
- name: orders
columns:
- name: order_amount
tests:
- positive_value
Advanced custom tests
Test with parameters:
{% test within_range(model, column_name, min_value, max_value) %}
select *
from {{ model }}
where {{ column_name }} < {{ min_value }}
or {{ column_name }} > {{ max_value }}
{% endtest %}
Usage:
tests:
- within_range:
min_value: 0
max_value: 100
Singular tests
One-off SQL tests for specific scenarios:
Create tests/assert_orders_have_customers.sql:
-- Ensure all orders have valid customers
select o.order_id
from {{ ref('orders') }} o
left join {{ ref('customers') }} c
on o.customer_id = c.customer_id
where c.customer_id is null
Singular tests fail if the query returns any rows.
Testing sources
Apply tests to raw source data:
version: 2
sources:
- name: raw_orders
database: raw_data
schema: ecommerce
tables:
- name: orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: status
tests:
- accepted_values:
values: ['pending', 'complete', 'cancelled']
Running tests
Run all tests
dbt test
Run tests for specific models
dbt test --select customers
dbt test --select customers orders
Run tests with specific tags
tests:
- unique:
config:
tags: ['critical']
dbt test --select tag:critical
Testing best practices
1. Test business-critical paths
Focus tests on data that drives decisions:
- Primary keys
- Foreign key relationships
- Financial calculations
- User-facing metrics
2. Balance coverage and performance
Too many tests slow pipelines. Prioritize:
- Uniqueness on keys
- Not_null on required fields
- Relationships on joins
- Business rules on critical metrics
3. Use appropriate severity
- error: Data correctness issues (uniqueness, relationships)
- warn: Data quality concerns (range checks, format validation)
4. Document test rationale
Add descriptions explaining why tests matter:
columns:
- name: revenue
description: Total order revenue. Must be positive.
tests:
- positive_value
5. Test incrementally
As models change, add tests incrementally rather than trying to achieve 100% coverage immediately.
Monitoring tests in dbt-Workbench
Test health dashboard
The dbt-Workbench catalog viewer displays:
- Overall test pass/fail rates
- Test health by model
- Historical test trends
- Recent failures
Test failure notifications
Configure alerts when tests fail:
- Set up Slack or email notifications
- Receive alerts for critical test failures
- Include failure details and model context
Debugging failed tests
When tests fail:
- View the failure in dbt-Workbench catalog
- Check the compiled test SQL
- Query failing records if store_failures is enabled
- Investigate upstream sources or model logic
Advanced testing patterns
Testing freshness
Ensure source data arrives on time:
sources:
- name: raw_orders
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
loaded_at_field: created_at
Testing snapshots
Validate snapshot logic:
{% test snapshot_validity(model) %}
select *
from {{ model }}
where dbt_valid_to < dbt_valid_from
{% endtest %}
Testing model contracts
Enforce column types and presence:
models:
- name: customers
config:
contract:
enforced: true
columns:
- name: customer_id
data_type: int
- name: email
data_type: varchar
Testing strategy by data layer
Sources
Test assumptions about raw data:
- Uniqueness on source keys
- Not_null on required fields
- Freshness checks
Staging models
Test data cleaning:
- Uniqueness after deduplication
- Type casting validations
- Relationship to sources
Intermediate models
Test transformation logic:
- Business rule validations
- Calculation checks
- Grain validations
Marts models
Test final outputs:
- Uniqueness at grain
- Required fields
- Metrics calculations
- Cross-model consistency
FAQ
What types of tests does dbt support?
dbt supports built-in tests (uniqueness, not_null, accepted_values, relationships), custom generic tests, and singular tests (one-off SQL queries).
How do I know if my tests are passing?
Run dbt test to execute all tests. In dbt-Workbench, view test health indicators in the catalog viewer and receive notifications for failures.
Can I set different severity levels for tests?
Yes. Tests can be configured with severity: warn or severity: error. Warn logs the issue but allows the run to continue, while error stops execution.
Should I test sources or just models?
Test both. Source tests catch upstream data issues early, while model tests ensure your transformations produce correct results.