首页/数据/数据工程与 ETL 速查/ETL 转换逻辑的测试方法
管道基础52 条

ETL 转换逻辑的测试方法

转换逻辑上线前用样本数据验证:对边界值(空值、负值、重复键、超长字符串)逐条断言,比上线后靠对账发现问题成本低得多。

转换逻辑为什么要写测试

ETL 转换逻辑的错误大多不是任务崩溃,而是静默算错:金额口径改了一位、join 少了一层过滤、空值被当作 0 参与求和。这类问题上线后要靠对账才能暴露,排查成本很高。把转换逻辑当作代码来对待——用脱敏样本数据做测试,对边界值逐条断言——是拦截这类低频但致命错误最便宜的手段。

基本信息

适用范围SQL/Python/Spark 转换逻辑、维表关联、口径变更
样本来源生产脱敏抽样,或手工构造边界用例
断言对象行数、主键唯一、字段非空、金额非负、枚举范围
运行方式pytest + pandas 本地跑,或在数仓内跑稽核 SQL

典型使用场景

  • 修改指标口径时,先跑测试确认历史口径未被带偏
  • join 逻辑调整(维表版本切换)后,验证未匹配率是否可接受
  • 金额、税率、折扣等计算列变更,用边界值锁定结果
  • 上线前回归:用最近一期真实数据跑一遍,对比行数与关键指标
  • 跨库同步后校验:源端与目标端主键集合是否一致

pytest 断言示例

pytest 断言示例
import pandas as pd

def load():                                  # 每个用例独立加载样本,失败时好定位
    return pd.read_parquet('/tmp/sample_dwd_order.parquet')

def test_amount_not_negative():
    df = load()
    assert (df['amount'] >= 0).all(), '存在负金额'    # 退款冲正需单独建模,不能混进本表

def test_order_id_unique():
    df = load()
    assert df['order_id'].is_unique, 'order_id 有重复' # 主键唯一是下游去重的前提

def test_no_null_status():
    df = load()
    assert df['status'].notna().all(), '状态为空会让枚举统计失真'

边界用例清单

用例输入特征期望表现
空值关键字段为 NULL 或空串按口径置默认值或丢弃,不能进分母
重复键同一主键出现两行去重后唯一,或直接触发告警
负值与零值金额为负、数量为 0按业务规则处理,不静默参与求和
超长字符串超出目标字段长度截断或报错,不能撑爆写入
时间边界跨天、跨年、时区偏移的时间戳归属日期按约定时区计算
把测试样本与断言纳入代码库,转换逻辑改动后先跑测试再提交,能拦住大部分低级口径错误;样本要小而覆盖边界,不要用整表数据跑,否则本地跑不动也没人愿意维护。
断言通过不等于业务正确:测试锁定的只是你写下的口径。口径本身理解错了,测试只会让错误稳定复现,关键指标的最终确认仍要和业务方对齐。

易错点

  • 测试样本只覆盖正常数据,上线后第一个报错往往就是空值或重复键
  • 只断言字段存在不断言业务语义(例如退款单在哪个字段标记),测试价值有限
  • 直接在真实生产表上跑用例,误写全表覆盖会污染线上数据
  • 样本含个人信息且未脱敏,随代码库扩散造成合规风险

数据来源与参考

  • 《Fundamentals of Data Engineering》数据质量与测试章节
  • pytest 官方文档
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌