Write the minimum dbt test set for a model
Start from the model's grain, then rank dbt tests by the cost of the failure they prevent: uniqueness and not-null on the grain key, relationships on foreign keys, accepted values on enums, and freshness on sources. You add one custom singular test, wire freshness on a source, and finish by running the set in data-aware CI against a staging schema.
At a glance
| When | A model's test set does not protect its grain key; A source has no freshness check while the extractor can stall silently |
|---|---|
| Where | dbt |
| Why | Uniqueness and freshness failures are the silent outages, and a test set ranked by blast radius catches them before consumers do. |
Learning objectives
- Rank dbt tests by blast radius instead of by effort
- Add a singular test for a rule schema.yml cannot express
- Wire freshness on a source and read its result
What you need
- A dbt project with at least one model
- models/schema.yml for generic tests
- [object Object]
Steps
- State a model's grain and match it to its unique key test. (from skill-dbt-model-review )
- Rank tests by the cost of the failure they prevent. (from skill-dbt-test-writer )
- Write a custom singular test. (from skill-dbt-test-writer )
- Wire freshness on a source. (from skill-dbt-test-writer )
- Run data-aware CI against a staging schema. (from project-dbt-warehouse-tests )
Check yourself
- schema.yml has unique + not_null on the declared grain key, and dbt test passes on the dev target.
- The source block carries freshness thresholds, and dbt source freshness runs green.
Worked example
Install the dbt-test-writer skill — from skill-dbt-test-writer
npx de-agent-platform add dbt-test-writer
Test set ranked by blast radius in schema.yml — from skill-dbt-test-writer
# Rank by blast radius, not by effort
models:
- name: <model>
columns:
- name: <grain key> # 1. unique + not_null - the model is meaningless without it
tests:
- unique
- not_null
- name: <foreign key> # 2. relationships - catches broken joins upstream
tests:
- relationships:
to: ref('<dimension model>')
- name: <enum column> # 3. accepted_values - catches upstream renames
tests:
- accepted_values:
values: [<allowed values>]
Run the test set with dbt build on the dev target — from project-dbt-warehouse-tests
dbt deps && dbt seed
dbt build --target dev
Sourced from
Security scan
Last sweep 2026-10-24: 0 finding(s), 0 marked as examples.
Common gotchas
- Row-count equality tests on raw sources: they flake.
- Testing a column nobody reads.
- `warn` severity on a correctness test nobody will see.
Production considerations
- Freshness on sources catches a stalled extractor, the #1 silent outage — run it on a schedule.
- Keep warn severity off correctness tests nobody will see.
Diagram
dbt test set
|
|- state grain --> unique + not_null on grain key
|
|- rank by blast radius --> relationships --> accepted_values --> freshness
|
|- singular test --> run in data-aware CI on staging schema
Registry stats
| Verification | community · Oct 21, 2026 |
|---|---|
| Completeness | 81 |
| Trust multiplier | ×1.25 |
| Composite | 34.15 · adequate |
| Freshness | 3 days since last source check |
| Difficulty | beginner |
| Version | 0.1.0 |
| License | CC-BY-4.0 |
| Security flags | none |
Did this solve your problem?
Write the minimum dbt test set for a model