Data Engineering  /  dbt

๐Ÿ”„ dbt โ€” Data Build Tool 60 guides ยท updated 2026

Analytics engineering with SQL โ€” models, tests, sources, and Jinja macros that turn raw warehouse tables into trustworthy, documented data products.

dbt test Command: Running Validation and Reading Failures Correctly

dbt test is the command that turns the schema tests and singular tests covered in dbt Tests from declarations into actual validation. Itโ€™s a separate command from dbt run deliberately โ€” building and validating are different concerns, and treating them separately is what lets you run tests without rebuilding everything, or rebuild without waiting on the full test suite during rapid local iteration.


Running the Full Test Suite

Terminal window
dbt test

Every generic test attached to every model, source, seed, and snapshot in the project runs, along with every singular test file in tests/. Output tells you pass/fail per test, plus how many rows failed for row-count-based failures:

1 of 6 START test not_null_stg_orders_order_id ......... [RUN]
1 of 6 PASS not_null_stg_orders_order_id ................ [PASS in 0.31s]
2 of 6 START test unique_stg_orders_order_id ............ [RUN]
2 of 6 FAIL 3 unique_stg_orders_order_id ................ [FAIL 3 in 0.42s]
...
Done. PASS=5 WARN=0 ERROR=0 FAIL=1 SKIP=0 TOTAL=6

Scoping Test Runs

Running every test in a large project on every local change is slow. Selection syntax scopes exactly what you want validated.

Terminal window
# Tests for one model
dbt test --select stg_orders
# Tests for a model and everything downstream
dbt test --select stg_orders+
# Only source tests
dbt test --select source:*
# Only tests of a specific type
dbt test --select test_type:singular
dbt test --select test_type:generic

test_type:singular and test_type:generic are particularly useful when debugging โ€” if you suspect the problem is in a custom business-logic check rather than a basic uniqueness violation, scoping to singular tests only cuts out noise from the (usually much larger) set of generic tests.


Storing Failures for Inspection

By default, a failed test tells you how many rows failed, but not which ones โ€” youโ€™d need to manually run the compiled test query to see them. --store-failures changes that, materializing the actual failing rows into a table you can query directly.

Terminal window
dbt test --select unique_stg_orders_order_id --store-failures
select * from analytics.dbt_test_audit.unique_stg_orders_order_id

This is enabled globally via config rather than per-invocation for teams that want it as a standing behavior:

dbt_project.yml
tests:
+store_failures: true
+schema: dbt_test_audit

For any project with more than a handful of models, store_failures turns โ€œa test failedโ€ from a debugging session into a two-second query against the exact rows that violated it.


Running Tests Against a Full Refresh

Tests inherently run against whatever data currently exists in the warehouse โ€” if a model hasnโ€™t been rebuilt since a fix, its tests will still reflect the old, broken data. A common local workflow when debugging a failing test:

Terminal window
dbt run --select stg_orders --full-refresh
dbt test --select stg_orders

Rebuilding before re-testing avoids the confusing scenario where youโ€™ve fixed the SQL but the test still fails because itโ€™s checking stale, previously-built data.


Indirect Selection: Testing Only What You Touched

By default, if you select a model with --select, dbt also runs any tests that reference multiple models where at least one is in your selection โ€” even if you didnโ€™t explicitly select the other model. This is called indirect selection, and it can be tuned:

Terminal window
# Only run tests fully contained within the selected models
dbt test --select stg_orders --indirect-selection=cautious
# Run tests as long as any referenced model is in the selection (default-ish behavior)
dbt test --select stg_orders --indirect-selection=eager

This matters most for relationships tests, which reference two models by nature โ€” the indirect selection mode determines whether changing one side of that relationship is enough to trigger the test, or whether both sides need to be explicitly selected.


Exit Codes for CI Integration

dbt test returns a non-zero exit code on any test failure, which is what makes it usable as a CI gate without any additional parsing logic โ€” a CI step running dbt test will correctly fail the pipeline the moment any test fails.

# Example CI step (pseudocode)
- run: dbt test
# Pipeline automatically fails here if dbt test returns non-zero
- run: deploy_to_production

Running Tests With Increased Verbosity for Debugging

When a testโ€™s default output isnโ€™t enough to understand why it failed, the --debug flag surfaces the actual compiled SQL dbt is executing for that test, alongside connection details โ€” useful when a test failure seems inconsistent with what youโ€™d expect from reading the YAML alone.

Terminal window
dbt --debug test --select unique_stg_orders_order_id

This prints the exact query dbt sent to the warehouse, which is often the fastest way to spot a subtle issue โ€” a test unexpectedly scoped to the wrong schema because of an environment misconfiguration, for instance, is usually obvious the moment you see the actual compiled query rather than just the pass/fail result.

Common Mistakes

Forgetting to rebuild before re-testing after a fix. Tests validate whateverโ€™s currently in the warehouse, not your latest, unbuild SQL changes.

Not using --store-failures on a project with frequent test failures. Without it, every failure investigation requires manually re-running the compiled test SQL โ€” a repeatable inefficiency that --store-failures eliminates entirely.

Misunderstanding indirect selection and being surprised by which tests ran (or didnโ€™t). If a relationships test unexpectedly ran (or didnโ€™t) after a scoped selection, check the indirect selection mode before assuming a bug in dbt itself.

Summary

FlagPurpose
--selectScopes which tests run
--store-failuresMaterializes failing rows into a queryable table
--indirect-selectionControls whether multi-model tests run on partial selections
Non-zero exit code on failureMakes dbt test usable directly as a CI gate

dbt test is where a projectโ€™s data quality claims get checked against reality on every run. Used with scoped selection and --store-failures during active development, it turns โ€œsomethingโ€™s wrong somewhereโ€ into a fast, specific, queryable answer.