dbt provides fine-grained control over which unit tests run and when. The key mechanism is the test_type:unit selector, which separates unit tests from data tests in all CLI commands. This separation is fundamental to how unit tests fit into your workflow — they run in different contexts (CI vs. production) and serve different purposes (logic verification vs. data health).
Running Unit Tests
# Run all unit testsdbt test --select test_type:unit
# Run all data tests (generic + singular)dbt test --select test_type:data
# Run all tests (unit + data)dbt testThe test_type:unit and test_type:data selectors are the primary way to separate the two categories. In your daily workflow, you’ll typically run unit tests during development (to verify your logic changes) and data tests after building models (to verify data health).
Filtering Unit Tests
dbt’s selector syntax works the same way for unit tests as it does for everything else:
# Run unit tests for a specific modeldbt test --select mrt__core__customers,test_type:unit
# Run unit tests with a specific tagdbt test --select tag:critical,test_type:unit
# Run a specific unit test by namedbt test --select test_mrt_core_customers_email_validationThe comma acts as an intersection — mrt__core__customers,test_type:unit means “tests that are both associated with mrt__core__customers AND are unit tests.” Without the test_type:unit qualifier, you’d also get the model’s generic data tests.
Running a test by name doesn’t need the test_type qualifier since the name is already unique.
Tag-based filtering is particularly useful during development. If you’ve tagged your tests by domain (finance, marketing, core), you can run just the tests relevant to your current work:
# Only run finance-related unit testsdbt test --select tag:finance,test_type:unitBuilding and Testing Together
The dbt build command runs models and tests in DAG order. By default, it includes both unit tests and data tests:
# Build models and run all tests (unit + data)dbt build
# Build but exclude unit testsdbt build --exclude-resource-type unit_testThe --exclude-resource-type flag (dbt 1.9+) is the clean way to skip unit tests during production builds. You can also set this as an environment variable for production environments:
export DBT_EXCLUDE_RESOURCE_TYPES=unit_testdbt buildThis is important because unit tests use mocked data and add no value in production. They belong in CI and local development only. Running them in production wastes compute and can cause confusion when mocked-data tests interact with real warehouse state.
Interpreting Output
A passing test is straightforward:
PASS test_mrt_core_customers_email_validationA failing test shows a diff between expected and actual output:
FAIL test_mrt_core_customers_email_validationGot: customer_id | is_valid_email 1 | falseExpected: customer_id | is_valid_email 1 | trueThe diff format makes it immediately clear what went wrong. In this case, the model returned false for is_valid_email when the test expected true. Either the model logic has a bug, or the test expectation is wrong.
For more complex failures — especially when the issue is in the generated SQL rather than the logic — add --debug:
dbt test --select test_mrt_core_customers_email_validation --debugThe --debug flag shows the full generated SQL, including the CTEs dbt creates for your mocked data. This is invaluable for diagnosing:
- Type mismatches between mock data and model output
- BigQuery STRUCT/ARRAY comparison issues
- Missing columns that the model expects but the mock doesn’t provide
- Macro override behavior
The Build-First Requirement
Unit tests need their upstream models’ schemas to exist in the database. If you’re running unit tests on a fresh environment (like a new CI runner), you’ll get “Not able to get columns for unit test” errors.
The fix: build upstream models first using --empty:
# Create schema-only versions of all upstream modelsdbt run --select +test_type:unit --empty
# Now run unit testsdbt test --select test_type:unitThe +test_type:unit selector (note the + prefix) selects all models upstream of models that have unit tests. The --empty flag builds tables with correct schemas but zero rows, which is cheap and fast.
This two-step pattern — build empty, then test — is the standard approach for CI/CD pipelines.
Common Selector Combinations
| Goal | Command |
|---|---|
| All unit tests | dbt test --select test_type:unit |
| All data tests | dbt test --select test_type:data |
| Unit tests for one model | dbt test --select model_name,test_type:unit |
| Critical unit tests only | dbt test --select tag:critical,test_type:unit |
| One specific test | dbt test --select test_name |
| Build without unit tests | dbt build --exclude-resource-type unit_test |
| Build upstream schemas for tests | dbt run --select +test_type:unit --empty |
These cover 95% of daily usage. The selector syntax is composable — you can combine model names, tags, test types, and graph operators (+ for upstream/downstream) to target exactly the tests you need.