用Python为接口自动生成测试用例
基于属性的测试 会产生大量的、随机的参数,特别适合为单元测试和接口测试生成测试用例
尽管早在2006年haskell语言就有了QuickCheck
来进行”基于属性的测试“,但是目前来看这依然是一个比较小众的领域,参考资料有限,本文如有不足,欢迎指正。
1. 基于表的测试
在过去的测试实践中,执行测试时通常需要明确的内容(Value):
条件
输入
结果
这些内容可以通过”判定树“或者”判断表“来表示,然后测试的执行过程变成了这样
“给定输入 X,我们期望 Y”
可以称为 基于表的测试
在最初,这给了我们测试的方向,但是缺点也非常明显:
你要足够多的"X->Y" 才能可能覆盖到隐蔽的bug。
这里请大家回答几个问题:
你是否已经为每一个测试编写了足够多的用例,以至于你十分确定真的不需要再增加用例了
这么多的用例,你编写的是否开心?是否高效?是否愿意继续坚持?
如果以上问题的答案不是yes,那么基于属性的测试 就是你需要掌握的东西!
2. 基于属性的测试
基于属性的测试和基于表的测试,最大的区别可以这样描述:
“给定输入 X值,我们期望 Y值”
vs
“给定输入 X类,我们期望 Y类”
于是利用工具生成大量的X类数据,进行测试,并验证结果是否Y类。
值得注意的是:
关注输入的类型,而不是输入的值
根据类型自动生成大量的、随机的输入值
换言之,它在自动的生成测试用例 ,虽然输入值是随机的,但是值得类型符合规范要求
3. 如何进行基于属性的测试
在不同的语言中有不同的工具来实现,比如:
haskell中的QuickCheck
、
java中的quicktheories
python中的hypothesis
本文以python为例进行演示:
假设有add函数,接收两个类型整数参数,并返回它们的相加结果
首先写出一个简单的测试用例
正如前面所说,一个这样的用例,根本没信心覆盖全部的场景,例如:
所以接下来怎么办?
改为基于属性的测试
执行结果
a =0 ,b=0 , c=0
a =0 ,b=0 , c=0
a =0 ,b=0 , c=0
a =16926 ,b=11053 , c=27979
a =0 ,b=0 , c=0
a =0 ,b=0 , c=0
a =21010 ,b=-2732672789497425072 , c=-2732672789497404062
a =0 ,b=0 , c=0
a =14554 ,b=-15956 , c=-1402
a =15597 ,b=0 , c=15597
a =15597 ,b=13 , c=15610
a =15597 ,b=0 , c=15597
a =15597 ,b=15597 , c=31194
a =28965 ,b=-36 , c=28929
a =113 ,b=-36 , c=77
a =28929 ,b=-36 , c=28893
a =28929 ,b=9356 , c=38285
a =9356 ,b=9356 , c=18712
a =10278 ,b=-62 , c=10216
a =-62 ,b=-62 , c=-124
a =1625 ,b=-68244995710046113596363052355575247332 , c=-68244995710046113596363052355575245707
a =1625 ,b=-13143 , c=-11518
a =1625 ,b=1625 , c=3250
a =-1940 ,b=-31868 , c=-33808
a =-7 ,b=1 , c=-6
a =-1 ,b=0 , c=-1
a =-1 ,b=0 , c=-1
a =13 ,b=7245 , c=7258
a =13 ,b=13 , c=26
a =-99 ,b=-18 , c=-117
a =-30172 ,b=66 , c=-30106
a =-30172 ,b=-16940 , c=-47112
a =-16940 ,b=-16940 , c=-33880
a =-1233214851 ,b=-5152 , c=-1233220003
a =-5152 ,b=-5152 , c=-10304
a =-16 ,b=-29706 , c=-29722
a =-29706 ,b=-29706 , c=-59412
a =-29706 ,b=116 , c=-29590
a =29696 ,b=90 , c=29786
a =29696 ,b=29696 , c=59392
a =-11446 ,b=-21185 , c=-32631
a =-21185 ,b=-21185 , c=-42370
a =-12 ,b=16437 , c=16425
a =-12 ,b=16437 , c=16425
a =3202 ,b=53 , c=3255
a =3202 ,b=3202 , c=6404
a =-98 ,b=3 , c=-95
a =82691970030325711417874227410289695610 ,b=1316378701 , c=82691970030325711417874227411606074311
a =82691970030325711417874227410289695610 ,b=82691970030325711417874227410289695610 , c=165383940060651422835748454820579391220
a =24100 ,b=14385 , c=38485
a =24100 ,b=14385 , c=38485
a =24100 ,b=24100 , c=48200
a =12293 ,b=-106 , c=12187
a =12293 ,b=27280 , c=39573
a =27280 ,b=27280 , c=54560
a =-18887 ,b=-5530 , c=-24417
a =-18887 ,b=-18887 , c=-37774
a =4738 ,b=122351151658095310625663643505383743930 , c=122351151658095310625663643505383748668
a =-24601163521689169516616964879873921492 ,b=565838202 , c=-24601163521689169516616964879308083290
a =4738 ,b=122351151658095310625663643281738736058 , c=122351151658095310625663643281738740796
a =-96098295006598318424285019062007505 ,b=25207226 , c=-96098295006598318424285019036800279
a =-96098295006598318424285019062007505 ,b=-1997122225172868107163535967078611096 , c=-2093220520179466425587820986140618601
a =-96098295006598318424285019062007505 ,b=-96098295006598318424285019062007505 , c=-192196590013196636848570038124015010
a =-36 ,b=1190 , c=1154
a =-36 ,b=-4 , c=-40
a =-36 ,b=-36 , c=-72
a =2047897602 ,b=-4641 , c=2047892961
a =2047897602 ,b=2047897602 , c=4095795204
a =-1307873608 ,b=11753 , c=-1307861855
a =-1307873608 ,b=-3308225400997338452 , c=-3308225402305212060
a =-1307873608 ,b=770256249 , c=-537617359
a =-1307873608 ,b=-1307873608 , c=-2615747216
a =-4715910568460396013 ,b=-18622 , c=-4715910568460414635
a =16754 ,b=-6053 , c=10701
a =-6053 ,b=-6053 , c=-12106
a =-6053 ,b=-6053 , c=-12106
a =-22264 ,b=44 , c=-22220
a =-22264 ,b=-22264 , c=-44528
a =-86 ,b=-86 , c=-172
a =-86 ,b=-86 , c=-172
a =1794 ,b=28170 , c=29964
a =1794 ,b=28170 , c=29964
a =-93 ,b=482 , c=389
a =-1 ,b=482 , c=481
a =-1 ,b=-1 , c=-2
由结果可知,工具根据参数是整数 这一规范,自动生成、执行了大量的测试用例
4. 在接口测试中自动生成用例
接口测试和函数的单元测试非常相似:
此外接口文档作为前后端、甚至测试开发的对接窗口,对参数的要求约定的更加细致,
以OpenAPI为例,每个参数可以有以下属性:
type:数字还是字符串?
format:密码还是电子邮箱?
maxLength:长度不超过多少?
required:是否必填?
in:参数通过什么传递?
其他...
于是为接口生成符合要求的参数就变得可行了,举个例子:
这是以unittest为例进行封装的结果,只需要在TestCase中指定openapi的内容(或路径),
启动测试框架时,会自动读取、解析接口文档,并生成测试用例
下面是部分执行日志,可以看到对接口发送了随机参数,并获得返回值
INFO 2022 -04-10 01:02:59 ,223 : 执行用例 login_login_access_token_post
INFO 2022 -04-10 01:02:59 ,223 : -----调用接口:login_login_access_token_post-----
INFO 2022 -04-10 01:02:59 ,223 : 发送请求>>> :请求数据 = {'userin' : {'password' : 'bEYtwDZUxdBVThEFOqTz' , 'email' : 'KEfQnAtTNtwZOXRjoIjp' }}
INFO 2022 -04-10 01:02:59 ,578 : 接收响应 <<<: <Response [400 ]>
INFO 2022 -04-10 01:02:59 ,578 : -----调用完毕:login_login_access_token_post-----
INFO 2022 -04-10 01:02:59 ,578 : 执行用例 test_token_login_test_token_post
INFO 2022 -04-10 01:02:59 ,578 : -----调用接口:test_token_login_test_token_post-----
INFO 2022 -04-10 01:02:59 ,578 : 发送请求>>> :请求数据 = {}
INFO 2022 -04-10 01:03:00 ,035 : 接收响应 <<<: <Response [200 ]>
INFO 2022 -04-10 01:03:00 ,035 : -----调用完毕:test_token_login_test_token_post-----
INFO 2022 -04-10 01:03:00 ,035 : 执行用例 sign_up_login_sign_up_post
INFO 2022 -04-10 01:03:00 ,035 : -----调用接口:sign_up_login_sign_up_post-----
INFO 2022 -04-10 01:03:00 ,035 : 发送请求>>> :请求数据 = {'userin' : {'password' : 'GgzYcOwapTwnkkKVTraE' , 'email' : 'EJrXlCSNKKjdiVvAOnTM' }}
INFO 2022 -04-10 01:03:00 ,657 : 接收响应 <<<: <Response [200 ]>
INFO 2022 -04-10 01:03:00 ,657 : -----调用完毕:sign_up_login_sign_up_post-----
INFO 2022 -04-10 01:03:00 ,657 : 执行用例 todo_list_todo_get
INFO 2022 -04-10 01:03:00 ,657 : -----调用接口:todo_list_todo_get-----
INFO 2022 -04-10 01:03:00 ,657 : 发送请求>>> :请求数据 = {}
INFO 2022 -04-10 01:03:01,233 : 接收响应 <<<: <Response [200 ]>
INFO 2022 -04-10 01:03:01,233 : -----调用完毕:todo_list_todo_get-----
INFO 2022 -04-10 01:03:01,233 : 执行用例 todo_post_todo_post
INFO 2022 -04-10 01:03:01,233 : -----调用接口:todo_post_todo_post-----
INFO 2022 -04-10 01:03:01,233 : 发送请求>>> :请求数据 = {'todoin' : {'title' : '' , 'is_done' : False }}
INFO 2022 -04-10 01:03:01,594 : 接收响应 <<<: <Response [200 ]>
INFO 2022 -04-10 01:03:01,594 : -----调用完毕:todo_post_todo_post-----
INFO 2022 -04-10 01:03:01,594 : 执行用例 todo_delete_all_todo_delete
INFO 2022 -04-10 01:03:01,594 : 执行用例 todo_get_todo__todo_id__get
INFO 2022 -04-10 01:03:01,594 : -----调用接口:todo_get_todo__todo_id__get-----
INFO 2022 -04-10 01:03:01,594 : 发送请求>>> :请求数据 = {'todo_id' : 2451 }
INFO 2022 -04-10 01:03:02,026 : 接收响应 <<<: <Response [404 ]>
INFO 2022 -04-10 01:03:02,026 : -----调用完毕:todo_get_todo__todo_id__get-----
INFO 2022 -04-10 01:03:02,026 : 执行用例 todo_put_todo__todo_id__put
INFO 2022 -04-10 01:03:02,026 : -----调用接口:todo_put_todo__todo_id__put-----
INFO 2022 -04-10 01:03:02,026 : 发送请求>>> :请求数据 = {'todo_id' : 1519 , 'todoin' : {'title' : '' , 'is_done' : False }}
INFO 2022 -04-10 01:03:02,423 : 接收响应 <<<: <Response [404 ]>
INFO 2022 -04-10 01:03:02,423 : -----调用完毕:todo_put_todo__todo_id__put-----
INFO 2022 -04-10 01:03:02,423 : 执行用例 todo_delete_todo__todo_id__delete
鉴于篇幅有限,暂时介绍这么多,
文章首发与我的公众号测试开发研习社,如果你对这个话题依旧有兴趣,可以回复或评论:PBT
原创不易,欢迎关注,不错过技术干货,谢谢鼓励!
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)