首页 > 其他分享 >编写自定义dbt通用数据测试

编写自定义dbt通用数据测试

时间:2024-10-18 20:17:54浏览次数:3  
标签:数据测试 通用 name 自定义 column tests 测试 model dbt

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 unique
  • not_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 %}

示例

官网提供了几个示例,有兴趣读者可以继续深入研究。

总结

本文介绍dbt内置通用测试,如何配置通用测试。在此基础上,重点介绍如何自定义通用测试,增强数据测试能力。期待您的真诚反馈,更多内容请阅读数据分析工程专栏。

标签:数据测试,通用,name,自定义,column,tests,测试,model,dbt
From: https://blog.csdn.net/neweastsun/article/details/142996703

相关文章

  • vue3中的自定义hooks的使用,以及和mixin的区别
    1、理解hooks的概念:hook本质是一个函数,将setup函数中使用的CompositionAPI进行封装,类似于Vue2中的mixin2、mixin相比hook的缺点:(1)变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护(2)同名属性、同名方法无法融合,可能会导致冲突3、例一:第一步:在src/hooks/index.js文件:imp......
  • 关于微信分享自定义标题,说明,图标基于PHP的功能实现
    1.首先先从微信公众平台获取AppId和AppSecret。不会的自行查询。比如要访问的服务器目录是www.xxxx.com。那么在这个目录下可以创建一个目录WeChat,在WeChat下分别创建文件access_token.json、config.php、jsapi_ticket.json、weChatShare.js。2.access_token.json和jsapi_ticke......
  • UI范式:创建自定义组件
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(MaoistLearning)➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/......
  • UI范式:页面和自定义组件生命周期
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(MaoistLearning)➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/......
  • 新建next.js项目,customize the default import alias是否要自定义默认导入别名(@/*)的选
    使用命令npxcreate-next-app@latest新建项目时,会自定义一些选项,如下图:其中自定义导入别名的选项,选择Yes或No有何区别?Wouldyouliketocustomizethedefaultimportalias(@/*)?...No/Yes一、选择"Yes"jsconfig.js文件的内容是:{"compilerOptions":{......
  • el-table自定义表头新
    <el-table-columnprop="address"label="333333"min-width="180":show-overflow-tooltip="true"><templateslot......
  • C#的自定义对话框和提示窗体 - 开源研究系列文章
          上次的应用因为需要用到对话框和提示窗体,然后系统自带的MessageBox界面个人又看不上,所以就想自己编写一个自定义的窗体,于是有了本文,具体的已经应用到笔者其它的应用里了。 1、项目目录;  2、源码介绍;1)实现;        2)......
  • CSDN-自定义公众号卡片
    ......
  • springboot使用自定义注解将对象注入容器中
    在SpringBoot中,你可以通过自定义注解和Spring的`BeanPostProcessor`来将对象注入到Spring容器中。以下是一个简单的实现步骤:1.**创建自定义注解**:importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.Reten......
  • 使用 Manim 绘制自定义函数曲线
    用动画来展示函数曲线,是一种非常直观、酷炫的方法。一、Manim简介Manim(MathematicalAnimationEngine)是一个用于创建数学动画的Python库。它允许您以编程的方式创建复杂的动画,包括函数曲线、几何图形和动画效果。Manim由3Blue1Brown(GrantSanderson)创建,他在YouTube上使......