Testing strategy and requirements for dbt models.
All models highly recommended to be declared in a schema.yml file.
Minimum requirement:
version: 2
models:
- name: my_model_nameOptional metadata (recommended):
version: 2
models:
- name: my_model_name
description: "What this model does"
tags: ['daily']
columns:
- name: column_name
description: "Column description"If your model uses unique keys, you should add tests:
dbt_utils.unique_combination_of_columnsfor the unique keynot_nullfor each column in the unique key
Model (ethereum_transactions.sql):
{{ config(
alias = 'ethereum_transactions'
, materialized = 'incremental'
, incremental_strategy = 'merge'
, unique_key = ['block_date', 'transaction_hash']
) }}Schema (schema.yml):
version: 2
models:
- name: ethereum_transactions
data_tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- block_date
- transaction_hash
columns:
- name: block_date
tests:
- not_null
- name: transaction_hash
tests:
- not_nullModel (user_balances.sql):
{{ config(
alias = 'user_balances'
, materialized = 'incremental'
, incremental_strategy = 'merge'
, unique_key = 'user_address'
) }}Schema (schema.yml):
version: 2
models:
- name: user_balances
data_tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- user_address
columns:
- name: user_address
tests:
- not_nullUse based on your data quality requirements:
unique- Single-column unique constraintsaccepted_values- Enum/categorical validationrelationships- Foreign key checks- Custom tests in
tests/directory - dbt native
unit_testsfor deterministic model logic checks with mocked inputs
Example:
columns:
- name: status
tests:
- accepted_values:
values: ['active', 'inactive', 'pending']
- name: user_id
tests:
- relationships:
to: ref('users')
field: id# Test all models
uv run dbt test
# Test specific model
uv run dbt test --select my_model
# Test only singular tests in tests/
uv run dbt test --select test_type:singular
# Test only dbt unit tests
uv run dbt test --select test_type:unit
# Test specific model and downstream
uv run dbt test --select my_model+
# Run and test in sequence
uv run dbt run --select my_model && uv run dbt test --select my_modelStore custom SQL tests in tests/ when a rule is business-specific and not covered by generic tests.
Current examples:
tests/labels/test_labels_balancer_v2_pools_category.sqltests/labels/test_labels_balancer_v3_pools_category.sql
These tests enforce that labels keep their expected category values.
Use dbt unit_tests for models that mostly combine or transform upstream model outputs deterministically.
Current examples are defined in:
models/_projects/balancer/labels/_schema.ymllabels_balancer_v2_pools_unions_chain_outputslabels_balancer_v3_pools_unions_chain_outputs
These tests verify that cross-chain union models preserve rows from upstream chain models.
- Pre-commit (Husky):
dbt parseonly (fast and zero-credit) - PR Quality CI (
dbt_quality.yml):dbt parseonly (fast and zero-credit) - PR Dune CI (
dbt_ci.yml): full integration runs/tests against Dune (credit-consuming, label-gated) - Prod workflows (
dbt_deploy.yml,dbt_prod.yml): deployment and incremental health checks
For incremental models:
unique_combination_of_columns- Catches duplicates from failed mergesnot_null- Prevents NULL key values that cause merge failures
NULL values in unique keys → merge lookups fail → duplicates inserted → data quality issues.
Pull requests run tests automatically. Failed tests block merging.
To fix test failures:
- Check test output for specific failures
- Query the model to investigate data
- Fix the underlying issue (not the test)
- Re-run tests locally before pushing
- dbt Best Practices - NULL handling in unique keys
- CI/CD - How tests run in GitHub Actions