Встроенные тесты: unique, not_null и другие

Тест данных — это утверждение о том, какими данные должны быть; dbt проверяет его автоматически.

Тест данных — декларативное правило (например «эта колонка уникальна»), которое dbt превращает в SQL-запрос: если он вернёт хоть одну строку-нарушитель, тест провален.

Зачем тестировать данные

Код тестируют, чтобы он не сломался. Данные тестируют по той же причине: первичный ключ задвоился, пришёл NULL там, где не должен, статус оказался неизвестным. Без тестов это всплывает в отчёте перед руководством. С тестами — ловится сразу при dbt test, до того как испорченные данные дойдут до дашборда.

Четыре встроенных теста

Тесты объявляют в YAML рядом с моделью — не в SQL:

# models/marts/_schema.yml
version: 2
models:
  - name: fct_orders
    columns:
      - name: order_id
        tests:
          - unique          # нет дублей
          - not_null        # нет пропусков
      - name: status
        tests:
          - accepted_values:
              values: ['paid', 'shipped', 'delivered', 'cancelled']
      - name: customer_id
        tests:
          - relationships:  # ссылочная целостность
              to: ref('dim_customers')
              field: customer_id
ТестЧто проверяет
uniqueВ колонке нет повторяющихся значений
not_nullВ колонке нет NULL
accepted_valuesВсе значения — из разрешённого списка
relationshipsКаждое значение есть в колонке другой модели (внешний ключ)

Запуск тестов

# Прогнать все тесты
dbt test

# Только тесты одной модели
dbt test --select fct_orders

Если тест падает, dbt покажет, сколько строк нарушили правило, и даст SQL, чтобы их посмотреть.

Как работает под капотом

Каждый тест dbt компилирует в SELECT, который ищет нарушителей. Тест unique превращается примерно в это:

CREATE TABLE fct_orders (order_id INTEGER, status TEXT);
INSERT INTO fct_orders VALUES (1, 'paid'), (2, 'shipped'), (2, 'paid');

-- так выглядит тест unique для order_id:
select order_id, count(*) as n
from fct_orders
group by order_id
having count(*) > 1;

Вывод:

order_id = 2, n = 2  — нарушитель, тест провален

Логика простая: тест проходит, если запрос-нарушитель вернул ноль строк. Это унифицирует все тесты — и встроенные, и ваши собственные.

Частые ошибки

  • Не тестировать ключи. unique + not_null на первичном ключе — минимум, который ловит самые опасные баги.
  • Забыть accepted_values на статусах/типах. Новое неожиданное значение тихо ломает логику ниже.
  • Игнорировать relationships. Без неё «осиротевшие» внешние ключи всплывают в джойнах как потерянные строки.

Итоги

  • Тесты объявляются в YAML и проверяют утверждения о данных: unique, not_null, accepted_values, relationships.
  • Под капотом каждый тест — это SQL, ищущий нарушителей; ноль строк = тест пройден.
  • dbt test ловит порчу данных до того, как она дойдёт до дашбордов.
Проверьте себя
1. Как dbt определяет, что тест данных пройден?
AЕсли запрос-нарушитель вернул ноль строк
BЕсли в таблице есть данные
CЕсли модель собралась без ошибок
DЕсли запрос выполнился быстро
2. Какой встроенный тест проверяет ссылочную целостность (внешний ключ)?
Aunique
Bnot_null
Crelationships
Daccepted_values
3. Где объявляются встроенные тесты dbt?
AВнутри SQL модели
BВ YAML рядом с моделью (например _schema.yml)
CВ profiles.yml
DВ packages.yml