If you’re using dbt to transform data, you’re already winning.
But did you know dbt has powerful testing features to keep your data clean, reliable, and trustworthy?
In this post, we’ll walk through:
- ✅ Basic dbt tests — the quick wins
- 🚀 Intermediate tests — custom logic and reusable macros
✅ Basic dbt Tests (Built-in)
dbt has out-of-the-box tests you can define in your .yml
files under your models. Here’s an example:
version: 2
models:
- name: customers
description: Customer master table
columns:
- name: customer_id
tests:
- not_null
- unique
- name: email
tests:
- not_null
🔧 What these do:
not_null
: ensures no NULLs in the columnunique
: makes sure values are uniqueaccepted_values
: (optional) lets you whitelist valuesrelationships
: checks if foreign keys match the referenced table
Example: accepted_values
- name: status
tests:
- accepted_values:
values: ['active', 'inactive', 'suspended']
These basic tests are easy to add and powerful for catching simple data issues — before they hit production dashboards.
🚀 Intermediate dbt Tests (Custom + Reusable)
Sometimes, built-in tests aren’t enough. You need logic like:
- Check for duplicate user records within a date range
- Validate order total = sum of line items
- Ensure one active subscription per user
That’s where custom SQL-based tests and macros come in.
🧪 Example: Custom SQL Test
Create a file in tests/
:
-- tests/one_active_subscription.sql
SELECT user_id
FROM {{ ref('subscriptions') }}
GROUP BY user_id
HAVING SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) > 1
Then reference it in your .yml
:
- name: user_id
tests:
- one_active_subscription
Boom! dbt will fail the build if a user has multiple active subs.
🧰 Bonus: Reusable Macros for Parametrized Tests
Say you want to check if a numeric column is always positive, across multiple models.
Create a macro in Jinja:
-- macros/test_positive_values.sql
{% test positive_values(model, column_name) %}
SELECT *
FROM {{ model }}
WHERE {{ column_name }} < 0
{% endtest %}
Then use it in .yml
:
- name: price
tests:
- positive_values
Now you have reusable logic that works like a built-in test!
Difference between dbt custom test and macro
A dbt custom SQL test is a one-off .sql file that contains a specific query to check a condition — it’s great for unique cases tied to a single model. In contrast, a reusable macro test is written once as a Jinja macro and can be applied across multiple models or columns with parameters, making it more flexible and DRY (Don’t Repeat Yourself). Use custom SQL for specific edge cases, and macros when you need consistency across your data tests.
🧵 Final Thoughts
Level | Tooling Used | Examples |
---|---|---|
Basic | Built-in YAML tests | not_null , unique , accepted_values |
Intermediate | Custom SQL/macros | positive_values , one_active_subscription |
Start small. Add not_null
and unique
everywhere.
Then gradually level up with custom logic where needed.
Your future self (and your data team) will thank you. And remember: “Broken data pipelines never lie — they just test your patience.”
Happy testing with dbt! 🧪