dbt’s testing has one property worth keeping. Tests live in git next to the models, they get code review, and dbt build interleaves models and tests, so a failed test stops the model downstream of it. That’s a real pipeline gate. Keep it.
The question is what it leaves uncovered.
What dbt tests cover, and what they leave to you
dbt ships four built-in generic tests: unique, not_null, accepted_values, and relationships. For sources, the dbt source freshness command checks how long ago each one last loaded. Everything past those you write yourself, for every column, in YAML or SQL, or configure from a package such as dbt-utils.
| Need | dbt out of the box | What fills the gap |
|---|---|---|
| Primary key is unique and present | unique, not_null | Nothing needed |
| Codes are on the allowed list | accepted_values | Nothing needed |
| Foreign keys resolve | relationships | Nothing needed |
| Values in a sane range, dates not in the future | Write each one | A generic test package, or generated tests |
| A column went from 2% null to 40% null | Write a threshold you guessed in advance | A test or monitor that learns from history |
| Table stopped loading, row count halved, column vanished | dbt source freshness catches a stale source against limits you set; row counts and dropped columns aren’t built in | Table monitors that learn from history |
| Vendor file broke before dbt ran | Outside dbt | Tests on the file or the landing table |
| Business rule, such as shipped never exceeding ordered | Write it | Someone who knows the business writes it, in any tool |
The last row doesn’t go away with any tool. Business rules are always yours.
dbt tests are enough when
Your problems live inside models you own. Your project is small enough that you can name every important column. Your sources are stable, or someone else watches them. And a failure that stops dbt build is the behavior you want. For a team running a few dozen models off one warehouse, that is a real and honest stopping point.
You need more when
The math stops working
In Where to Actually Start with DataOps in 2026, DataKitchen works it through: a bronze layer of 100 tables averaging 10 columns needs roughly 200 table tests and 2,000 column tests, and silver and gold want their own. The Guide to DataOps Tests totals a Medallion architecture at roughly 5,000 tests and prices it: writing half of them by hand, 2,500 tests at 30 minutes each, is 1,250 hours. So the keys get tested and the rest doesn’t.
Data breaks before dbt sees it
Vendor CSV files, API pulls, and loads from other teams arrive upstream of your models. dbt tests the models it builds, and your data also passes through Airflow, Python scripts, and BI extracts.
You need history, not a guess
A hand-set threshold is a number you typed. A monitor derives the expected row count or null rate from each table’s own past, which is the only way to cover thousands of tables. dbt’s built-ins don’t do that. Even dbt source freshness compares the last load against limits you set; it doesn’t learn them. Packages like Elementary add anomaly detection on top of dbt, and Boomi bought Elementary on October 19, 2026; Elementary says the open-source package continues.
Adding to dbt without replacing it
Keep your dbt tests as the gate inside dbt build. Then point a generator at the same database. DataOps TestGen profiles every column across 55 characteristics, generates tests from 34 test types, and adds freshness, volume, and schema monitors, all running as SQL in your warehouse. DataKitchen’s estimate is that generation covers about 80 percent of the tests you need. The remaining 20 percent is business logic, and that part you were always going to write.