Кастомные и singular-тесты
Когда встроенных тестов мало — пишут свои: разовые singular или переиспользуемые generic.
Singular-тест — отдельный SQL-запрос-проверка для конкретного случая. Generic-тест — параметризуемый тест-макрос, который можно навешивать на любые колонки, как встроенные.
Singular: проверка «на один раз»
Иногда нужно проверить специфичное бизнес-правило, которого нет среди встроенных: «сумма заказа не бывает отрицательной», «дата доставки не раньше даты заказа». Для этого пишут singular-тест — просто .sql-файл в папке tests/, возвращающий строки-нарушители.
-- tests/assert_amount_non_negative.sql
select order_id, amount
from {{ ref('fct_orders') }}
where amount < 0
Правило то же, что у встроенных: если запрос вернул строки — тест провален. Здесь нашлись бы заказы с отрицательной суммой.
Проверим логику singular-теста
CREATE TABLE fct_orders (order_id INTEGER, amount NUMERIC);
INSERT INTO fct_orders VALUES (1, 100), (2, -5), (3, 250);
-- тело singular-теста: ищем нарушителей
select order_id, amount
from fct_orders
where amount < 0;
Вывод:
order_id = 2, amount = -5 — нарушитель, тест провален
Generic: свой переиспользуемый тест
Если правило нужно на многих колонках (например «значение положительное»), его делают generic — макросом, который принимает модель и колонку:
-- tests/generic/positive_value.sql
{% test positive_value(model, column_name) %}
select {{ column_name }}
from {{ model }}
where {{ column_name }} <= 0
{% endtest %}
Теперь его навешивают в YAML, как встроенный:
models:
- name: fct_orders
columns:
- name: amount
tests:
- positive_value
Singular против generic
| Singular | Generic | |
| Что это | SQL-файл в tests/ | Тест-макрос с параметрами |
| Переиспользование | Нет, под один случай | Да, на любые колонки |
| Когда брать | Уникальная разовая проверка | Повторяющееся правило |
Как работает под капотом
Оба вида сводятся к одному принципу: запрос, возвращающий нарушителей. Singular — это готовый запрос; generic — шаблон запроса, в который dbt подставит конкретную модель и колонку при компиляции. dbt test прогоняет и встроенные, и кастомные тесты одинаково и считает тест пройденным при нуле строк. Так качество данных становится таким же кодом, как и сами модели, — лежит в git, проходит ревью.
Частые ошибки
- Писать singular там, где правило повторяется. Если проверка нужна на многих колонках — делайте generic.
- Возвращать из теста «хорошие» строки. Тест должен возвращать именно нарушителей; ноль строк = успех.
- Не покрывать бизнес-правила. Встроенные тесты не знают вашу логику — специфику закрывают кастомные тесты.
Итоги
- Singular-тест — разовый SQL-запрос в
tests/, возвращающий нарушителей. - Generic-тест — параметризуемый макрос, навешиваемый на колонки как встроенный.
- Оба следуют правилу «ноль строк = пройден» и хранятся как код в git.