# dbt_utils: the generic tests and macros most dbt projects end up depending on

> dbt_utils is a dbt package of reusable macros, generic tests and one materialization, published on dbt Hub and consumed by projects as a dependency. It is infrastructure rather than a framework, and the version split at dbt Core 1.10.6 will decide how you write every test you add.

**dbt-labs/dbt-utils** — Utility functions for dbt projects.

- Repository: https://github.com/dbt-labs/dbt-utils
- Website: https://hub.getdbt.com/dbt-labs/dbt_utils/latest/
- Stars: 1,834 · Forks: 644
- Language: Makefile
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/dbt-labs-dbt-utils

## The arguments key decision comes before any test you write

The most consequential thing in the README is a version note, not a feature. The recommended approach going forward is to nest all test arguments under `arguments:` if you use dbt Core 1.10.6 or higher, tied to the `require_generic_test_arguments_property` flag in dbt Core's global config.

The three cases are spelled out. On dbt Core below 1.10.6 you remove the `arguments:` key and list the test arguments directly under the test. On dbt Core 1.10.6 or higher you can set them either under `arguments:` as in the examples or at the top level. If you use Fusion, the `arguments:` key must be used.

The required YAML shape depends on the dbt Core version and on whether you use Fusion.

## Sixteen generic tests and what each one asserts

The generic tests are the reason most projects take the dependency. Each is a SQL file under macros/generic_tests and is attached to a model by name.

`equal_rowcount` asserts two relations have the same number of rows, taking a `compare_model` reference. `fewer_rows_than` asserts the model has fewer rows than the one it compares. Both accept `group_by_columns`.

`equality` is the most configurable of the set. It compares two relations and can take a `compare_columns` list to restrict the comparison, an `exclude_columns` list to skip known-divergent fields, and a `precision` argument to compare numeric columns at a given precision. That combination covers the common reconciliation case where a model is supposed to match a source except for one identifier and some rounding.

`expression_is_true` asserts a SQL expression holds for all records, which the README points at integrity checks across columns: verifying an outcome from arithmetic between columns, verifying column length, verifying a truth value. It also accepts an optional `where` argument so the assertion applies to a subset of records.

The rest are narrower: `recency`, `at_least_one`, `not_constant`, `not_empty_string`, `cardinality_equality`, `not_null_proportion`, `not_accepted_values`, `relationships_where`, `mutually_exclusive_ranges`, `sequential_values`, `unique_combination_of_columns` and `accepted_range`. `not_null_proportion` is the one worth singling out, since it asserts a proportion rather than an absolute, which is how you express a quality bar that tolerates a known amount of missing data.

## Introspective macros read metadata instead of scanning data

The introspective group answers questions about the warehouse itself rather than about rows. `get_column_values` returns the distinct values in a column. `get_filtered_columns_in_relation` returns column names matching a pattern. `get_relations_by_pattern` and `get_relations_by_prefix` find relations by name. `get_query_results_as_dict` returns query results as a dictionary. `get_single_value` collapses a query to one value.

These are the macros that make loops possible in dbt, because they let a macro enumerate what exists before generating SQL for it. A model that pivots, unions or generates columns per table can discover the tables first. The tradeoff is that metadata queries hit the catalog rather than the data, so they are cheap on large warehouses, but the result depends on what the adapter has cached, and the README documents no cache-invalidation behavior for `get_relations_by_pattern`.

## The SQL generators carry the cross-database weight

This group exists because warehouse dialects disagree. The README has a section for cross-database macros, and the generator list shows why: `date_spine`, `generate_series`, `generate_surrogate_key`, `pivot`, `unpivot`, `width_bucket`, `haversine_distance`, `union_relations`, `star`, `group_by`, `deduplicate`, `safe_add`, `safe_subtract` and `safe_divide`.

The arithmetic ones, `safe_add`, `safe_subtract` and `safe_divide`, exist because integer division and null propagation differ between engines. `width_bucket` and `haversine_distance` exist because the native functions are not portable. `date_spine` and `generate_series` exist because date table generation is a per-dialect affair that every project otherwise rewrites.

The practical consequence is that adding dbt_utils reduces the number of adapter-specific branches in your project, but it also means a behavior change inside the package changes model SQL for every model that uses it. Generators are macros that emit SQL, not tested units you can reason about in isolation, so an upgrade here deserves a full `dbt build` rather than a spot check.

## Web macros and Jinja helpers fill a smaller niche

Three web macros parse a URL: `get_url_parameter`, `get_url_host` and `get_url_path`. The Jinja helpers are `pretty_time`, `pretty_log_format`, `log_info` and `slugify`.

The logging helpers are the pragmatic ones. `pretty_time` and `pretty_log_format` exist so a `run_started_at` or `execution_time` field in `meta` renders as something readable instead of a raw timestamp, which is what you want when those values surface in a dbt docs page or a job run summary.

The web macros are narrower than the name suggests. They parse a URL you already have, which is useful for splitting a landing page URL into host and path for a source model, and that is about it. There is no fetching, no HTTP client and no retry logic here.

## install_by_period is the only materialization

Everything else in the package is a macro or a generic test. There is exactly one materialization, `insert_by_period`, which is what makes dbt_utils usable as an incremental strategy rather than only as a helper library. A materialization is a Jinja block that controls how a model is built, so this one changes how rows are written over time rather than what a query computes.

The rest of the macros operate at query level. If you are looking for something that reshapes your incremental logic, this package is not the place; if you are looking for assertions and cross-dialect SQL, it is.

## Running the package's own tests

The repository is a working dbt project with a Makefile, and the interesting part is that `target` is parameterized. The test target runs `tox -e dbt_integration_$(target)`, the dev target installs `dbt-$(target)` in pre-release mode along with dev-requirements.txt, and the setup-db target brings up Postgres with docker-compose for system testing.

In other words the adapter is chosen by passing target, and `make test target=postgres` is the shape of an invocation. Supporting `target=postgres` is confirmed by docker-compose.yml, which defines a single service:

```yaml
services:
  postgres:
    image: cimg/postgres:17.5
    environment:
      - POSTGRES_USER=root
    ports:
      - "5432:5432"
```

There is also a supported_adapters.env file in the repository, which is the place to look for which adapters CI exercises, and tox.ini and pytest.ini for the Python-level test harness.

## What dbt_utils does not give you

Two boundaries are worth being explicit about.

First, this is a macro package, so its behavior is bound to the dbt Core version you run. The `require_generic_test_arguments_property` change described above is a dbt Core behavior change that this package had to accommodate, and there will be more. Test YAML written for one generation of dbt Core is not portable to the next without edits, and the package cannot shield you from that because the flag lives in dbt's global config, not here.

Second, the generic tests assert on rows that already exist. They run after your model materializes, so a failure can identify an unexpected result in the materialized model. `equality` against a compare model is the most useful of these for catching silent drift between a source and a derived table, and it is the one most likely to be skipped in a hurry because it doubles as a reconciliation query.

The maintenance picture is healthy, with the last push on 2026-10-01 and releases 1.4.1 on 2026-06-28, 1.4.0 on 2026-06-18 and 1.2.1 on 2026-01-13. The license is Apache-2.0, which imposes no obligation on your project's license. The installation instruction points at the dbt Hub page for current installation instructions rather than hardcoding a command.

## Conclusion

dbt_utils is worth taking as a dependency for the generic tests and cross-database macros it already maintains, particularly the equality and row count comparisons you would otherwise hand-write per adapter. It is not worth patching, since the package follows dbt's macro conventions and version behavior, so pin a release and read CHANGELOG.md on upgrade. Before adding tests, check your dbt Core version: from 1.10.6, arguments can be nested under `arguments:` or placed at the top level, while Fusion requires `arguments:`.

## FAQ

### How do I install dbt_utils?

The README does not hardcode an install command. It points at the dbt Hub page for dbt-labs/dbt_utils for the current installation instructions, and at dbt's package management docs for general guidance on installing packages.

### What is the arguments key in dbt_utils tests?

It is a property for nesting test arguments, recommended if you use dbt Core 1.10.6 or higher, linked to the `require_generic_test_arguments_property` flag. On older dbt Core you remove the key and list arguments directly, and if you use Fusion the key is mandatory.

### How do I compare two models in dbt_utils?

Use the `equality` generic test with a `compare_model` argument. You can restrict it with `compare_columns`, skip columns with `exclude_columns`, and set `precision` to compare numeric columns at a given precision. `equal_rowcount` is the narrower option when you only care about row counts.

### Which databases does dbt_utils support?

The package provides cross-database macros and SQL generators such as date_spine, generate_series, generate_surrogate_key, pivot, unpivot, width_bucket, haversine_distance, safe_add, safe_subtract and safe_divide, which exist to avoid per-dialect SQL. A supported_adapters.env file in the repository records which adapters the test suite exercises, and local system testing in the Makefile is set up against Postgres only.

## Sources

- [dbt-labs/dbt-utils on GitHub](https://github.com/dbt-labs/dbt-utils)
- [License: Apache-2.0](https://github.com/dbt-labs/dbt-utils/blob/main/LICENSE)
- [Project website](https://hub.getdbt.com/dbt-labs/dbt_utils/latest/)
- [README](https://github.com/dbt-labs/dbt-utils/blob/main/README.md)
- [Releases](https://github.com/dbt-labs/dbt-utils/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dbt-labs-dbt-utils
