ETL测试框架

本文介绍了一个针对数据仓库ETL流程的新型测试框架,该框架采用Ruby语言开发,旨在提高测试用例的可读性和灵活性。相比传统的SQL测试方法,新框架能够更直观地展现源数据与目标数据间的映射关系,并支持复杂逻辑处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近写了一个针对数据仓库ETL的测试框架,baidu google了一下发现还没有非常靠谱的同类型框架或解决方案,就忍不住提前分享一下(其实是因为周五下午不想干活)。

首先分享一下我们过去测试ETL的方法:很简单,就是写两段SQL分别query上下两层数据,然后通过数据库的minus方法来得到不符合预期的数据,进而进行分析。例如

-- Source

select
src1.pk
, case
when src1.lkp_ky is null then
-2 -- not available
else
case
when src2.some_ky is null then
0 -- not found
else
src2.some_ky
end
end some_ky
from
(select * from source_table_1 where lgcl_del_fg = 'n') src1
left join
source_table_2 src2 on src1.lkp_ky = src2.lkp_ky

minus

-- Target

select
pk
, some_ky
from
target_table


用SQL写测试用例是可行的的,但是有很多我认为不够好的问题:
1. SQL可读性非常差。
一个ETL的mapping有十几二十个字段很正常,写出来的SQL最后一看超过二百行也很正常,但是如果能让别人一眼看懂就不正常了:对于数据集之间的连接,是在from下面进行,而对于连接好的数据的操作,是在select下面,from上面进行(即上例中的case when语句),这种憋屈的语法结构会让review的人很头疼,导致后期维护也会痛不欲生。

2. SQL写ETL逻辑的时候有点捉急……
SQL毕竟不能完全算是编程语言,虽然提供了很多数据的操作方法,但是比起正儿八经的编程语言还是略逊一筹……相信用SQL测过ETL的人都有过力不从心的感觉。我认为这也是ORM出现的根本原因!

3. SQL一点都不灵活!
这点一时不知道怎么说才好,因为我根本不知道SQL跟灵活有什么关系。不过看完我们提供的方案后,希望读者能感受到:这TMD才是灵活!

总之篇幅关系,我就不继续埋汰SQL了,重点还是介绍我们的方案:提供一套取代SQL的方法来编写测试用例。

先看一下我们实现上面sql的方式:

mapping("test_etl") do

declare_target_table 'target_table', :t
declare_source_table "select * from source_table_1 where lgcl_del_fg='n'", :src1

m t.pk, src1.pk

m t.some_ky do
declare_source_table 'source_table_2', :src2
left_join src2, "src1.lkp_ky = sr2.lkp_ky"
if src1.lkp_ky.nil?
-2 # not available
else
if src2.some_ky.nil?
0 # not found
else
src2.some_ky
end
end
end

end


这里说明一下,框架用ruby开发,用例就是ruby代码,如果看官您不懂ruby,请不要转身离开,我们做了最够多的工作在框架本身,暴露给编写用例的测试人员的都是最基础的,既普通的if else一类的控制语句以及字符串数字的操作,所以完全不用为了使用此框架来额外学习很多ruby知识。

开始解释我们的用例:
mapping表示着我们用例的开始,后面的“test_etl”相当于这个用例的名字,后面生成报告时会用到这个名字。mapping后面的do ... end中编写我们一个mapping的所有逻辑。

declare_source(target)_table 方法用来定义我们使用到的表(或者sql),第一个参数是表名,第二个参数是别名,定义好别明后,下面的代码即可应用别名来代替包。
比如declare_source_table "wo_qu_nian_mai_le_ge_biao", :biao
之后就可以用biao.id, biao.name 来表示表的id跟name字段。(顺便一提,我们还提供了CTE的定义。)

m方法用来表示target表的column跟source是怎么对应的。如果没有任何处理直接照搬过去可以用“m t.pk, src1.pk”来表示。
如果要经过转换才能得到目标column,可以把转换逻辑写在m后面的 do end里,如

m t.some_ky do
declare_source_table 'source_table_2', :src2
left_join src2, "src1.lkp_ky = sr2.lkp_ky"
if src1.lkp_ky.nil?
-2 # not available
else
if src2.some_ky.nil?
0 # not found
else
src2.some_ky
end
end
end


do end里是纯的ruby代码(其中的left_join是框架自己的方法),可以非常清晰的表达转换逻辑。

看到这里可能有人会说:这不是换汤不换药么?看不出比上面那段SQL强在哪里。
首先我们的用例可以直接把source target的mapping关系非常直观的表现出来,其次,我们能做到join表跟使用表写在一起,即我能清楚的知道我join这张表是干什么用的。如果用例需要join非常多的表,这种设计对于用例可读性的提升是巨大的!同时如我之前说的,do end里面可以用ruby代码写逻辑,而ruby比SQL操作数据轻松,比如ruby可以轻松实现字符串的split功能,据我所知oracle目前还没有提供split功能。‘

当然这还不是全部,我刚才还说我们的方案非常灵活。

1. 首先可以非常方便添加参数:比如再etl实际运行中可能会用到sysdate(系统时间),但是测试运行时sysdate很可能跟之前sysdate不一样,我们测试时要赋一个值给sysdate,这时我们就可以把sysdate作为一个参数。

2. 可以添加动态的变量:比如我们每次测试时需要找到最新的数据,即每次都要得到一个max(date),这个过程可以非常方便得定义在用例里。

3. 可以引用外部数据:测试过程中有可能需要读取一个文件的数据,或者访问webservice得到数据,这个过程也可以定义到用例中!只要ruby能解决的问题,框架都能解决……

4. 引用之前的计算结果:写sql时,经常发现之前得到的计算结果,后面居然没有办法直接用。如我的source有一个学生的各科成绩,首先我要得到学生的总分,然后要根据学生的总分来判断学生是优等生还是差生。如果用sql的话我要这样写:

select
std_id
, (score1 + score2 + score3 .... + scoren) total_score
, case
when (score1 + score2 + score3 .... + scoren) >= 600 then
'NB'
when (score1 + score2 + score3 .... + scoren) >= 400 (score1 + score2 + score3 .... + scoren) < 600 then
'Normal'
else
'SB'
end grade
from
score

当然也可以用CTE

with t as(
select
std_id
, (score1 + score2 + score3 .... + scoren) total_score
from
score)
select
std_id
, t.total_score total_score
, case
when t.total_score >= 600 then
'NB'
when t.total_score >= 400 t.total_score < 600 then
'Normal'
else
'SB'
end grade
from
score inner join t on score.std_id = t.std_id


嗯,再看看我们怎么做的

mapping("score_grade") do

declare_target_table 'target', :t
declare_source_table "score", :score

m t.std_id, score.std_id

m t.total_score do
score.score1 + score.score2 + score.score3 ... + scoren
end

m t.grade do
case
when row[:total_score] >= 600
'NB'
when row[:total_score] < 600 && row[:total_score] >= 400
'Normal'
else
'SB'
end
end

end


请容我先自我陶醉一会……

现在简单介绍一下我认为最牛逼的功能:测试覆盖率
我们的方案可以检测到数据是否能覆盖所有分支!!!从而能彻底杜绝很多传统测试方法难以检查到到的隐患。

请让我再陶醉一会……

现在我们提供了一套完全替代sql的书写用例方法,这种用例书写方法不仅灵活,而且可读性极强。我做这个框架的灵感就来源于看到设计文档时,想如果文档能当用例运行起来就好了,现在我做到了……我们的用例跟ETL的设计文档相似度已经很高了。

用例管理/运行/数据验证/报告生成方面,包括给user提供扩展的接口我们也做了,但是感觉没有太大的新意,就不详细介绍了。

我们的框架现在还在测试中,如果稳定了,我会把gem(ruby的代码包)pull到rubygems.org,还会有更详细的文档介绍上面提到或者没提到的功能,如果大家有兴趣可以联系我一起讨论。部分代码可以在https://github.com/piecehealth/ETLTester 得到
python+opencv简谱识别音频生成系统源码含GUI界面+详细运行教程+数据 一、项目简介 提取简谱中的音乐信息,依据识别到的信息生成midi文件。 Extract music information from musical scores and generate a midi file according to it. 二、项目运行环境 python=3.11.1 第三方库依赖 opencv-python=4.7.0.68 numpy=1.24.1 可以使用命令 pip install -r requirements.txt 来安装所需的第三方库。 三、项目运行步骤 3.1 命令行运行 运行main.py。 输入简谱路径:支持图片或文件夹,相对路径或绝对路径都可以。 输入简谱主音:它通常在第一页的左上角“1=”之后。 输入简谱速度:即每分钟拍数,同在左上角。 选择是否输出程序中间提示信息:请输入Y或N(不区分大小写,下同)。 选择匹配精度:请输入L或M或H,对应低/中/高精度,一般而言输入L即可。 选择使用的线程数:一般与CPU核数相同即可。虽然python的线程不是真正的多线程,但仍能起到加速作用。 估算字符上下间距:这与简谱中符号的密集程度有关,一般来说纵向符号越稀疏,这个值需要设置得越大,范围通常在1.0-2.5。 二值化算法:使用全局阈值则跳过该选项即可,或者也可输入OTSU、采用大津二值化算法。 设置全局阈值:如果上面选择全局阈值则需要手动设置全局阈值,对于.\test.txt中所提样例,使用全局阈值并在后面设置为160即可。 手动调整中间结果:若输入Y/y,则在识别简谱后会暂停代码,并生成一份txt文件,在其中展示识别结果,此时用户可以通过修改这份txt文件来更正识别结果。 如果选择文件夹的话,还可以选择所选文件夹中不需要识别的文件以排除干扰
### Java ETL Framework Options 在数据集成领域,ETL(Extract, Transform, Load)框架扮演着重要角色。尽管许多现代工具如Pentaho Kettle主要基于其他编程语言实现,但仍然存在一些专注于Java生态系统的解决方案。以下是几个常见的Java ETL框架选项: #### 1. **Talend Open Studio** Talend是一个功能强大的开源ETL工具,支持多种数据源之间的复杂转换操作。它提供了图形化界面以及底层的Java API接口供开发者调用。其核心组件完全采用Java构建,因此非常适合熟悉该语言的技术人员使用[^3]。 ```java // Example of using Talend's tFileInputDelimited component programmatically. import org.talend.components.api.component.runtime.BulkExec; public class MyCustomJob { public static void main(String[] args){ BulkExec.execute("tFileInputDelimited_1", new Object[]{"/path/to/input.csv"}); } } ``` #### 2. **JasperReports Server with ETL Capabilities** 除了作为报表服务器外,Jaspersoft还集成了部分ETL特性来处理简单的数据流任务。借助iReport Designer或者直接编码方式都可以完成基本的数据抽取和加载工作。由于整个产品线均围绕Java技术栈打造所以易于被现有团队接受并维护[^4]. #### 3. **Apache Camel** 虽然严格意义上讲Camel并非专用于ETL场景的设计模式集合体;然而凭借灵活路由定义机制加上广泛适配器库的支持完全可以胜任此类职责。特别是当面对异构系统间交互需求时表现尤为突出——这一切都建立于纯正血统的标准版SE之上无需额外依赖任何第三方插件即可运行良好[^5]. ```xml <!-- Sample Apache Camel Route Configuration --> <route> <from uri="file:/input?noop=true"/> <split> <tokenize token="\n"/> <to uri="bean:transformerBean"/> </split> <to uri="jdbc:dataSourceName"/> </route> ``` #### 4. **Spring Batch** 最后不得不提的就是来自轻量级应用容器家族成员之一—Batch项目本身虽定位于批处理范畴之内但实际上涵盖了相当一部分传统意义上的ELT流程要素比如分页读取远程数据库记录再经过一系列业务逻辑加工之后最终持久化至目标位置等等皆可通过声明式XML配置文件轻松搞定而且全程保持高度可测试性便于后期扩展升级[^6]. --- 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值