基于业务层面数据库造数据工具

Python
Linux/Windows
guolong123 · September 23, 2020 · 4844 次阅读 · 11 条评论

dbfaker

介绍

基于数据库层面批量生成有逻辑关联的数据

starDownloadspypi version

软件架构

对于要造大量数据来讲,有几种方式,一是通过开发写的接口来模拟真实用户场景来产生数据,这种方式在某些方面来讲是比较合适的,比如因为是模拟用户场景,业务数据完整,不会丢.但不好的地方就是要处理的地方太多,接口定义,签名,加密等等,还有扩展性也很不好,一个业务场景要写一套代码.另一种方式,是已知业务产生的数据之间的依赖关系后,直接在数据库中插入相关数据,本项目就是通过这种方式来实现,好处就是生成规则通过配置文件来描述即可(yaml 文件),不需要额外添加代码(对于某些字段生成规则有可能需要单独编写方法),与测试库 testrunner 比较类似.现在已知有些库也支持直接在数据库内造数据,但对库表之间的关联关系的处理都做得不太好.

另外对于测试人员进行自动化接口测试时,前置数据是个问题,是通过业务接口一条条跑完作为前置数据产生条件还是直接在数据库里面插入需要的数据呢?前面一种方式在业务场景复杂的时候用例的维护可能会变得极其麻烦。比如:

你要测试一个用户加入商品到购物车的一个接口;前置数据有哪些?

  1. 用户账号
  2. 商品

通过接口来造数据的流程如下:

  1. 通过用户端注册接口来创建账号
  2. 通过管理端接口登录,拿到 token(预设管理端账号是已经存在了,如果不存在你还得注册管理端账号)
  3. 登陆后通过管理端接口创建商品信息

这三个步骤看起来简单,但实际上针对某些业务场景还有接口加密、签名、而且几乎每一条测试用例都需要用到 N 个前置条件,这些通过接口来调用的前置条件只要有一个失败就会影响你真正要测试的接口。做过接口测试的同学应该深有体会;

实际上实现上述接口测试,真正要测的只是商品是否能正常加入到购物车;真正依赖的就只有两个数据,用户账号和商品信息。我们只需要在用户表和商品表里面各插入一条数据就可以了。通过本工具,只需要描述两个表的每个字段的生成规则就可以直接在数据库中插入两条 “十分真实” 的数据。用这种方式来解决测试用例前置条件的问题不说是最好的方式,但肯定是比上面的前置用例的方式好很多。

安装

git clone https://gitee.com/guojongg/dbfaker.git
cd dbfaker
# 可先创建虚拟环境后再安装
python3 setup.py install
# 使用pip直接安装
pip3 install dbfaker

# 卸载
pip uninstall dbfaker

使用说明

1. 生成 meta.yml 文件,可通过内置工具 table2yml.py 来一键生成模板,生成后需完善模板中表字段的定义

table2yml.py 文件使用说明:

usage: table2yml [-h] [--connect [CONNECT]] [--table_names [TABLE_NAMES]]
                 [--sql_file [SQL_FILE]] [--output [OUTPUT]]
                 [type]

数据库表转数据生成yaml文件格式工具

positional arguments:
  type                  数据来源,table_name: 通过输入表名与数据库链接方式,在数据库中获取数据库建表语句;
                        table_statement: 指定数据库建表语句的sql文件路径

optional arguments:
  -h, --help            show this help message and exit
  --connect [CONNECT]   数据库连接语法,例如:mysql+mysqldb://pdmsadmin:system001@cpcs.ho
                        melabs.in/pdms_hospital
  --table_names [TABLE_NAMES]
                        数据库表,多个表以“,”分割
  --sql_file [SQL_FILE]
                        数据库建表语句的sql文件路径
  --output [OUTPUT]     输出文件名,默认为数据库表名+meta.yml


2, 编辑 meta.yml 文件,文件格式如下

package:
  - datetime  # 导入额外的包,在jinja2模板中使用(下面有用到datetime包,所以要先导入)
env:
  id:
    engine: faker.uuid
    rule: null
  time_format:
    engine: faker.eq
    rule:
      value: "%Y-%m-%d %H:%M:%S"
tables:
- columns:
  - column: id
    comment: 数据主键id
    engine: faker.eq
    rule:
      value: '{{ env.id }}'  # 通过引用环境变量中的值
  - column: name
    comment: 姓名
    engine: faker.name
    rule: null
  - column: idcard
    comment: 身份证号
    engine: faker.ssn
    rule: null
  - column: age
    comment: 年龄
    engine: faker.eq
    rule:
      value: '{{ datetime.datetime.now().year - int(stu.idcard[6:10]) }}'  # 通过jinja2模板直接计算
  - column: sex
    comment: 性别
    engine: faker.eq
    rule:
      value: '{{ "man" if int(stu.idcard[-2]) % 2==1 else "female" }}'  # 通过jinja2模板直接计算
  comment: ''
  table: stu
- columns:
  - column: id
    comment: 数据主键id
    engine: faker.uuid
    rule: null
  - column: stu_id
    comment: 数据主键id
    engine: faker.eq
    rule:
      value: '{{ stu.id }}'  # 通过其他表中的值
  - column: course_name
    comment: 课程名称
    engine: faker.choice # 通过内置方法从列表中随机取一个值
    rule:
      value: [数学,语文,英语,化学,地理]
  - column: course_time
    comment: 上课时间
    engine: faker.now  # 通过内置方法获取当前时间,并按照指定格式返回
    rule:
      format: "{{ env.time_format }}"
  comment: '课程信息 '
  table: course

3,创建sql

PC:~/01 Work/07 MyProject/dbfaker$ source venv/bin/activate
(venv) PC:~/01 Work/07 MyProject/dbfaker$ dbfaker -h
usage: dbfaker [-h] [-n [NUMBER]] [-i] [-c [CONNECT]] [-o [OUTPUT]] [-p]
               [--project_name PROJECT_NAME]
               [meta_file]

通过yml格式的描述文件来生成数据

positional arguments:
  meta_file             yml文件所在路径

optional arguments:
  -h, --help            show this help message and exit
  -n [NUMBER], --number [NUMBER]
                        生成数据数量
  -i, --insert          是否插入到数据库
  -c [CONNECT], --connect [CONNECT]
                        数据库连接语法,例如:mysql+mysqldb://pdmsadmin:system001@cpcs.ho
                        melabs.in/pdms_hospital
  -o [OUTPUT], --output [OUTPUT]
                        指定文件名,输出内容到文件
  -p, --_print          是否打印到控制台
  --project_name PROJECT_NAME
                        初始化项目时的项目名称


# 打印输出
dbfaker data/test.yml --number 10 -p

# 保存到文件
dbfaker data/test.yml --number 10 -o out.sql

# 插入到数据库
dbfaker data/test.yml --number 10 -i --connect mysql+mysqldb://pdmsadmin:system001@cpcs.homelabs.in/pdms_hospital

通过上述模板文件生成出 sql:

(venv) guolong@guolong-PC:~/01Work/07MyProject/dbfaker$ dbfaker data/test.yml -p -n 10
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10/10 [00:00<00:00, 121.26/s]
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('873ebda283ad418681427eee33599d88','常斌','320311197109145812','49','man');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('c642936a6a3d4067a0bdeaa738e777f7','873ebda283ad418681427eee33599d88','英语','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('975a595bdbe44bca9066e899cf095fdf','朱超','371403194412033153','76','man');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('e7fce0bd705d407d82365c5078e18e9f','975a595bdbe44bca9066e899cf095fdf','语文','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('c7a35fc6034b416b84633aba68f948a5','谭桂花','220881193603124667','84','female');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('9ec067ab61384b82839f037d19046d54','c7a35fc6034b416b84633aba68f948a5','地理','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('ec50b43857894724a8520fe4267cdb1c','黄颖','430424200102150376','19','man');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('a82ca7d9dfb14084ac59f3eef81c397d','ec50b43857894724a8520fe4267cdb1c','语文','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('ada7f3efbd6c44cbbb2b4e97f1e2192e','崔芳','110102197508318809','45','female');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('09068ec48e344513aa13248afc2a6c9a','ada7f3efbd6c44cbbb2b4e97f1e2192e','语文','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('c7be9316a52d4ce6813e2522c06ec244','伍建','530424195111204807','69','female');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('b525533bd784470898075b6c3c98e319','c7be9316a52d4ce6813e2522c06ec244','化学','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('ab8156d118ce47388ec080d4ca182324','张杰','430722199410179304','26','female');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('df802e97f23e43c6b8329f3bc80532b4','ab8156d118ce47388ec080d4ca182324','化学','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('dc508ae92cd14c2b8b44f881e9a2a9dc','温玲','361030193711149093','83','man');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('37a72421f0ec419aa181cb38f3216866','dc508ae92cd14c2b8b44f881e9a2a9dc','语文','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('48ddceaacddf43fd850a750c003f65e4','杨芳','530322197311251166','47','female');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('083d692d6a35483a9f4c77d99cb74abc','48ddceaacddf43fd850a750c003f65e4','语文','2020-10-21 16:39:07');
INSERT INTO `stu` (`id`,`name`,`idcard`,`age`,`sex`) VALUES ('f1e6ff166e504f4b9a1c4ef5c2791e2d','万帅','321323197507311448','45','female');
INSERT INTO `course` (`id`,`stu_id`,`course_name`,`course_time`) VALUES ('47e6cbb2999c433fb15f2102eec95295','f1e6ff166e504f4b9a1c4ef5c2791e2d','地理','2020-10-21 16:39:07');
执行完成,共生成10组数据

更多例子

usage example(使用举例)

内置方法一览

dbfaker 内置方法速览

自定义方法使用说明

dbfaker 自定义方法使用说明

觉得还可以,帮忙点个赞!!!

如果有意见或建议或者遇到相关问题需要探讨解决方案欢迎给我提:Issue

httpmock 工具有需要的可以参考这个地址

https://gitee.com/guojongg/http-faker

评论列表
AllisonLiu 发表于 June 20, 2022

感谢大佬的倾情分享,这个工具库可以用于大多数少量造数的场景,但可能对大批量造数不是很友好,是不是可以支持一下自定义数据的生成条目数?另外支持的数据库类型也比较有限,目前也是只支持 mysql 和 sqlite,是不是可以拓展下呢?

guolong123 发表于 December 22, 2020

@xuwangcheng14
这个看实际应用了,业务复杂的确不太适合自己插入数据。但此工具实际上是提供了一个通用的方式,相对于直接调用接口来讲通用性比较强。而对于简单的业务,相对于 sql 直接插入来讲,这个工具可以让数据更加真实,在某些需要展示的场景下比较合适。如果只是测试功能,不要求数据合理性,那直接 sql 也是一种好的方式。

徐汪成 发表于 December 22, 2020

实际上,简单点的业务我们都是手动写 SQL 脚本来实现造数据,但是业务比较复杂的,真的,光靠写 SQL 插数据不太实现,因为有时候业务涉及到的表可能超过 20 张 +,而且还会涉及到数据同步,指令,后台进程等等。
所以建议涉及 10 张表之内的业务,没有其他程序干扰的可以使用工具进行 SQL 插入,超过 10 张表的建议直接走接口,在复杂点的比较我上面说到的,建议使用 Jmeter 或者 loadrunner 录制脚本。

心意已决 发表于 November 25, 2020

1234

guolong123 发表于 October 09, 2020

@gratester
那说明此类工具应该还是有意义的。

da-pengTT 发表于 October 07, 2020

https://testerhome.com/topics/23013 我之前也开发过一个造数据工具,使用 Excel 进行字段规则编写

陈恒捷 发表于 September 27, 2020

额,入口是我前面截图里面的 最新收录开源项目 。我用箭头指一下:

顶部的是最热项目,点赞数多才会上去的。

guolong123 发表于 September 27, 2020

@chenhengjie123 是的,此工具造数据的前提知道数据库表中的逻辑关系,相比通过接口来造,更通用。写这个目的本身也是公司有个需要造 “真实数据” 来做业务功能对外展示用的需求。刚开始也是通过接口来造的,后面发现没法扩展,才用这种方式的。

陈恒捷 发表于 September 26, 2020

PS:直接插库类工具,实际项目中会有一些缺点。
如果漏了插入表,很容易成为脏数据引起后续一连串的其妙问题;同时对于微服务类的(每个服务有自己独立的 database),也不是太好用。

所以调用接口还是目前比较主流的造数方式,相对稳定可靠。当然对于一些接口逻辑依赖了第三方服务不好造的,在熟悉逻辑的前提下,直接插库也是一种很好的方式。

陈恒捷 发表于 September 25, 2020

在这里:

guolong123 发表于 September 25, 2020

为何发布审核通过了却没任何入口展示呢?