dbt 默认提供了 Not Null, Unique, Relationships, 和Accepted Values 四个通用数据测试,这些测试被称为 ”schema 测试“ ,底层这些通用测试就是类似宏的测试块。本文首先介绍内置通用测试,然后介绍如何自定义通用测试,最后还实践如何覆盖内置通用测试的功能。
内置数据测试能力
单个数据测试
定义数据测试的最简单方法是编写返回失败记录的精确SQL。我们称这些为“单一”数据测试,因为它们是用于单一目的的一次性断言测试。
这些测试在.sql文件中定义,通常在测试目录中。你可以在测试定义中使用Jinja(包括ref和source),就像在创建模型时一样。每个.sql文件包含select语句,负责定义数据测试:
# tests/assert_total_payment_amount_is_positive.sql
-- Refunds have a negative amount, so the total amount should always be >= 0.
-- Therefore return records where total_amount < 0 to make the test fail.
select
order_id,
sum(amount) as total_amount
from {{ ref('fct_payments') }}
group by 1
having total_amount < 0
这个测试的名称是文件的名称:assert_total_payment_amount_is_positive,很简单。单个数据测试很容易编写——简单到你可能会发现自己反复编写相同的基本结构,只更改列或模型的名称。到那时,测试就不是那么单一了!这样的话,我们建议编写通用测试。
标准通用测试
某些数据测试是通用的:它们可以一次又一次地被重用。在测试块中定义通用数据测试,该测试块包含参数化查询并接受参数。它可能看起来像:
{% test not_null(model, column_name) %}
select *
from {{ model }}
where {{ column_name }} is null
{% endtest %}
我们注意到有两个参数,model和column_name,它们随后被模板化为查询。这就是使测试“通用”的原因:可以在任意多的列上定义它,可以跨任意多的模型定义它,dbt将相应地传递model和column_name的值。一旦定义了通用测试,就可以将其作为属性添加到任何现有模型(source, seed, 或snapshot)上,这些属性被添加到与资源相同目录下的.yml文件中。
dbt提供了四种已经定义的通用数据测试:unique、not_null、accepted_values和relationships。下面是在订单模型上使用这些测试的完整示例:
version: 2
models:
- name: orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: status
tests:
- accepted_values:
values: ['placed', 'shipped', 'completed', 'returned']
- name: customer_id
tests:
- relationships:
to: ref('customers')
field: id
简单解释如下:
unique
: 在orders
模型中的order_id
列必须是唯一的should be uniquenot_null
: 在orders
模型中order_id
列值不能包括null值accepted_values
: 在orders
模型中status
列的值应该在给定字典中的一项:'placed'
,'shipped'
,'completed'
, or'returned'
relationships
: 在orders
模型中 每个customer_id
列值都在模型customers
表的id
列中 (也称引用完整性)
在幕后,dbt使用来自通用测试块的参数化查询,为每个数据测试构造一个选择查询。这些查询返回你的断言不为真的行;如果测试返回零行,则断言通过。
带标准参数的通用测试
通用测试在sql文件中定义,这些文件能在两个目录中:
tests/generic/
: 在 测试路径 (默认为tests/
)下的特定子目录中。macros/
: 通用测试的工作方式与宏非常相似,之前这是唯一可以定义通用测试的地方。如果你的通用测试依赖于复杂的宏逻辑,那么与宏放在同一文件中定义宏会更方便。
为了自定义通用测试,需创建测试块,命名为 <test_name>
。所有通用测试应该接受一个或两个标准参数:
- model:定义测试的资源,模板化关系名。(注意,参数总是命名为model,即使资源是source, seed, snapshot,也是如此)
- column_name:定义测试的列。并非所有通用测试都在列级别上操作,但是如果测试列,则应该接受column_name作为参数。
下面是使用两个参数的is_even schema测试示例:
# tests/generic/test_is_even.sql
{% test is_even(model, column_name) %}
with validation as (
select
{{ column_name }} as even_field
from {{ model }}
),
validation_errors as (
select
even_field
from validation
-- if this is true, then even_field is actually odd!
where (even_field % 2) = 1
)
select *
from validation_errors
{% endtest %}
如果这个select语句返回0条记录,那么提供的模型参数中的每条记录都是偶数!如果返回的记录数非零,则model中至少有一条记录为奇数,测试失败。
若要使用此通用测试,请在source, snapshot, or seed的tests属性中按名称指定它:
# models/<filename>.yml
version: 2
models:
- name: users
columns:
- name: favorite_number
tests:
- is_even
在本例中,用一行代码就创建了一个测试!将‘model’参数传递给is_even测试,而favorite_number
作为column_name
参数传递进来。你可以为其他列、其他模型添加相同的行定义测试。它们都将使用相同的通用测试给你的项目资源添加新的测试。
带有额外参数的通用测试
is_even测试无需指定任何其他参数即可工作。其他测试,比如relationships,需要的不仅仅是model和column_name。如果你的自定义测试需要其他非标准参数,可以在测试签名声明中,就像下面示例中的field和to参数:
# tests/generic/test_relationships.sql
{% test relationships(model, column_name, field, to) %}
with parent as (
select
{{ field }} as id
from {{ to }}
),
child as (
select
{{ column_name }} as id
from {{ model }}
)
select *
from child
where id is not null
and id not in (select id from parent)
{% endtest %}
当从.yml文件调用该测试时,给对应参数提供实际值。注意,标准参数(model和column_name)是由上下文提供的,因此不需要再次定义。
# models/<filename>.yml
version: 2
models:
- name: people
columns:
- name: account_id
tests:
- relationships:
to: ref('accounts')
field: id
通用测试带缺省配置值
在通用测试定义中可以包含config()
块。这里设置的值将为该通用测试的所有特定实例设置默认值,除非在特定实例的.yml属性中被覆盖。
# tests/generic/warn_if_odd.sql
{% test warn_if_odd(model, column_name) %}
{{ config(severity = 'warn') }}
select *
from {{ model }}
where ({{ column_name }} % 2) = 1
{% endtest %}
任何时候使用warn_if_odd
测试,它总是具有警告级别的严重性,除非特定的测试覆盖了该值:
# models/<filename>.yml
version: 2
models:
- name: users
columns:
- name: favorite_number
tests:
- warn_if_odd # default 'warn'
- name: other_number
tests:
- warn_if_odd:
severity: error # overrides
自定义dbt 内置测试
要更改内置通用测试的工作方式——无论是添加额外的参数、重新编写SQL,还是出于任何其他原因——只需将名为<test_name>的测试块添加到你自己的项目中,DBT将支持你的版本而不是全局实现!
# tests/generic/<filename>.sql
{% test unique(model, column_name) %}
-- whatever SQL you'd like!
{% endtest %}
示例
官网提供了几个示例,有兴趣读者可以继续深入研究。
- Creating a custom schema test with an error threshold
- Using custom schema tests to only run tests in production
- Additional examples of custom schema tests
总结
本文介绍dbt内置通用测试,如何配置通用测试。在此基础上,重点介绍如何自定义通用测试,增强数据测试能力。期待您的真诚反馈,更多内容请阅读数据分析工程专栏。
标签:数据测试,通用,name,自定义,column,tests,测试,model,dbt From: https://blog.csdn.net/neweastsun/article/details/142996703