用Python为接口自动生成测试用例

基于属性的测试会产生大量的、随机的参数,特别适合为单元测试和接口测试生成测试用例

尽管早在2006年haskell语言就有了QuickCheck来进行”基于属性的测试“,但是目前来看这依然是一个比较小众的领域,参考资料有限,本文如有不足,欢迎指正。


1. 基于表的测试

在过去的测试实践中,执行测试时通常需要明确的内容(Value):

  1. 条件
  2. 输入
  3. 结果

这些内容可以通过”判定树“或者”判断表“来表示,然后测试的执行过程变成了这样

“给定输入 X,我们期望 Y”

可以称为 基于表的测试

在最初,这给了我们测试的方向,但是缺点也非常明显:

你要足够多的"X->Y" 才能可能覆盖到隐蔽的bug。

这里请大家回答几个问题:

  1. 你是否已经为每一个测试编写了足够多的用例,以至于你十分确定真的不需要再增加用例了
  2. 这么多的用例,你编写的是否开心?是否高效?是否愿意继续坚持?

如果以上问题的答案不是yes,那么基于属性的测试就是你需要掌握的东西!


2. 基于属性的测试

基于属性的测试和基于表的测试,最大的区别可以这样描述:

“给定输入 X值,我们期望 Y值”

vs

“给定输入 X类,我们期望 Y类”

于是利用工具生成大量的X类数据,进行测试,并验证结果是否Y类。

值得注意的是:

  1. 关注输入的类型,而不是输入的值
  2. 根据类型自动生成大量的、随机的输入值

换言之,它在自动的生成测试用例,虽然输入值是随机的,但是值得类型符合规范要求


3. 如何进行基于属性的测试

在不同的语言中有不同的工具来实现,比如:

  • haskell中的QuickCheck
  • java中的quicktheories
  • python中的hypothesis

本文以python为例进行演示:

假设有add函数,接收两个类型整数参数,并返回它们的相加结果

  1. def add(a: int, b: int) -> int:
  2. pass

首先写出一个简单的测试用例

  1. def test_add():
  2. assert 3 == add(1, 2)

正如前面所说,一个这样的用例,根本没信心覆盖全部的场景,例如:

  • 参数是0
  • 参数是负数
  • 参数值特别大
  • 其他...

所以接下来怎么办?

改为基于属性的测试

  1. import hypothesis.strategies as st
  2. from hypothesis import given
  3. @given(st.integers(), st.integers())
  4. def test_add(a, b):
  5. c = add(a, b)
  6. print(f"{a=},{b=}, {c=}")
  7. assert isinstance(c, int)
  8. assert c == a + b

执行结果

  1. a=0,b=0 c=0
  2. a=0,b=0 c=0
  3. a=0,b=0 c=0
  4. a=16926,b=11053 c=27979
  5. a=0,b=0 c=0
  6. a=0,b=0 c=0
  7. a=21010,b=-2732672789497425072 c=-2732672789497404062
  8. a=0,b=0 c=0
  9. a=14554,b=-15956 c=-1402
  10. a=15597,b=0 c=15597
  11. a=15597,b=13 c=15610
  12. a=15597,b=0 c=15597
  13. a=15597,b=15597 c=31194
  14. a=28965,b=-36 c=28929
  15. a=113,b=-36 c=77
  16. a=28929,b=-36 c=28893
  17. a=28929,b=9356 c=38285
  18. a=9356,b=9356 c=18712
  19. a=10278,b=-62 c=10216
  20. a=-62,b=-62 c=-124
  21. a=1625,b=-68244995710046113596363052355575247332 c=-68244995710046113596363052355575245707
  22. a=1625,b=-13143 c=-11518
  23. a=1625,b=1625 c=3250
  24. a=-1940,b=-31868 c=-33808
  25. a=-7,b=1 c=-6
  26. a=-1,b=0 c=-1
  27. a=-1,b=0 c=-1
  28. a=13,b=7245 c=7258
  29. a=13,b=13 c=26
  30. a=-99,b=-18 c=-117
  31. a=-30172,b=66 c=-30106
  32. a=-30172,b=-16940 c=-47112
  33. a=-16940,b=-16940 c=-33880
  34. a=-1233214851,b=-5152 c=-1233220003
  35. a=-5152,b=-5152 c=-10304
  36. a=-16,b=-29706 c=-29722
  37. a=-29706,b=-29706 c=-59412
  38. a=-29706,b=116 c=-29590
  39. a=29696,b=90 c=29786
  40. a=29696,b=29696 c=59392
  41. a=-11446,b=-21185 c=-32631
  42. a=-21185,b=-21185 c=-42370
  43. a=-12,b=16437 c=16425
  44. a=-12,b=16437 c=16425
  45. a=3202,b=53 c=3255
  46. a=3202,b=3202 c=6404
  47. a=-98,b=3 c=-95
  48. a=82691970030325711417874227410289695610,b=1316378701 c=82691970030325711417874227411606074311
  49. a=82691970030325711417874227410289695610,b=82691970030325711417874227410289695610 c=165383940060651422835748454820579391220
  50. a=24100,b=14385 c=38485
  51. a=24100,b=14385 c=38485
  52. a=24100,b=24100 c=48200
  53. a=12293,b=-106 c=12187
  54. a=12293,b=27280 c=39573
  55. a=27280,b=27280 c=54560
  56. a=-18887,b=-5530 c=-24417
  57. a=-18887,b=-18887 c=-37774
  58. a=4738,b=122351151658095310625663643505383743930 c=122351151658095310625663643505383748668
  59. a=-24601163521689169516616964879873921492,b=565838202 c=-24601163521689169516616964879308083290
  60. a=4738,b=122351151658095310625663643281738736058 c=122351151658095310625663643281738740796
  61. a=-96098295006598318424285019062007505,b=25207226 c=-96098295006598318424285019036800279
  62. a=-96098295006598318424285019062007505,b=-1997122225172868107163535967078611096 c=-2093220520179466425587820986140618601
  63. a=-96098295006598318424285019062007505,b=-96098295006598318424285019062007505 c=-192196590013196636848570038124015010
  64. a=-36,b=1190 c=1154
  65. a=-36,b=-4 c=-40
  66. a=-36,b=-36 c=-72
  67. a=2047897602,b=-4641 c=2047892961
  68. a=2047897602,b=2047897602 c=4095795204
  69. a=-1307873608,b=11753 c=-1307861855
  70. a=-1307873608,b=-3308225400997338452 c=-3308225402305212060
  71. a=-1307873608,b=770256249 c=-537617359
  72. a=-1307873608,b=-1307873608 c=-2615747216
  73. a=-4715910568460396013,b=-18622 c=-4715910568460414635
  74. a=16754,b=-6053 c=10701
  75. a=-6053,b=-6053 c=-12106
  76. a=-6053,b=-6053 c=-12106
  77. a=-22264,b=44 c=-22220
  78. a=-22264,b=-22264 c=-44528
  79. a=-86,b=-86 c=-172
  80. a=-86,b=-86 c=-172
  81. a=1794,b=28170 c=29964
  82. a=1794,b=28170 c=29964
  83. a=-93,b=482 c=389
  84. a=-1,b=482 c=481
  85. a=-1,b=-1 c=-2

由结果可知,工具根据参数是整数这一规范,自动生成、执行了大量的测试用例


4. 在接口测试中自动生成用例

接口测试和函数的单元测试非常相似:

  • 输入数据
  • 返回数据

此外接口文档作为前后端、甚至测试开发的对接窗口,对参数的要求约定的更加细致,

以OpenAPI为例,每个参数可以有以下属性:

  • type:数字还是字符串?
  • format:密码还是电子邮箱?
  • maxLength:长度不超过多少?
  • required:是否必填?
  • in:参数通过什么传递?
  • 其他...

于是为接口生成符合要求的参数就变得可行了,举个例子:

  1. import logging
  2. import unittest
  3. import requests
  4. from api_tools import APITestCase
  5. class UserCase(APITestCase):
  6. schema = "http://127.0.0.1:7600/openapi.json"
  7. if __name__ == "__main__":
  8. unittest.main()

这是以unittest为例进行封装的结果,只需要在TestCase中指定openapi的内容(或路径),

启动测试框架时,会自动读取、解析接口文档,并生成测试用例

下面是部分执行日志,可以看到对接口发送了随机参数,并获得返回值

  1. INFO 2022-04-10 01:02:59,223 : 执行用例 login_login_access_token_post
  2. INFO 2022-04-10 01:02:59,223 : -----调用接口:login_login_access_token_post-----
  3. INFO 2022-04-10 01:02:59,223 : 发送请求>>> :请求数据 = {'userin': {'password': 'bEYtwDZUxdBVThEFOqTz', 'email': 'KEfQnAtTNtwZOXRjoIjp'}}
  4. INFO 2022-04-10 01:02:59,578 : 接收响应 <<<: <Response [400]>
  5. INFO 2022-04-10 01:02:59,578 : -----调用完毕:login_login_access_token_post-----
  6. INFO 2022-04-10 01:02:59,578 : 执行用例 test_token_login_test_token_post
  7. INFO 2022-04-10 01:02:59,578 : -----调用接口:test_token_login_test_token_post-----
  8. INFO 2022-04-10 01:02:59,578 : 发送请求>>> :请求数据 = {}
  9. INFO 2022-04-10 01:03:00,035 : 接收响应 <<<: <Response [200]>
  10. INFO 2022-04-10 01:03:00,035 : -----调用完毕:test_token_login_test_token_post-----
  11. INFO 2022-04-10 01:03:00,035 : 执行用例 sign_up_login_sign_up_post
  12. INFO 2022-04-10 01:03:00,035 : -----调用接口:sign_up_login_sign_up_post-----
  13. INFO 2022-04-10 01:03:00,035 : 发送请求>>> :请求数据 = {'userin': {'password': 'GgzYcOwapTwnkkKVTraE', 'email': 'EJrXlCSNKKjdiVvAOnTM'}}
  14. INFO 2022-04-10 01:03:00,657 : 接收响应 <<<: <Response [200]>
  15. INFO 2022-04-10 01:03:00,657 : -----调用完毕:sign_up_login_sign_up_post-----
  16. INFO 2022-04-10 01:03:00,657 : 执行用例 todo_list_todo_get
  17. INFO 2022-04-10 01:03:00,657 : -----调用接口:todo_list_todo_get-----
  18. INFO 2022-04-10 01:03:00,657 : 发送请求>>> :请求数据 = {}
  19. INFO 2022-04-10 01:03:01,233 : 接收响应 <<<: <Response [200]>
  20. INFO 2022-04-10 01:03:01,233 : -----调用完毕:todo_list_todo_get-----
  21. INFO 2022-04-10 01:03:01,233 : 执行用例 todo_post_todo_post
  22. INFO 2022-04-10 01:03:01,233 : -----调用接口:todo_post_todo_post-----
  23. INFO 2022-04-10 01:03:01,233 : 发送请求>>> :请求数据 = {'todoin': {'title': '', 'is_done': False}}
  24. INFO 2022-04-10 01:03:01,594 : 接收响应 <<<: <Response [200]>
  25. INFO 2022-04-10 01:03:01,594 : -----调用完毕:todo_post_todo_post-----
  26. INFO 2022-04-10 01:03:01,594 : 执行用例 todo_delete_all_todo_delete
  27. INFO 2022-04-10 01:03:01,594 : 执行用例 todo_get_todo__todo_id__get
  28. INFO 2022-04-10 01:03:01,594 : -----调用接口:todo_get_todo__todo_id__get-----
  29. INFO 2022-04-10 01:03:01,594 : 发送请求>>> :请求数据 = {'todo_id': 2451}
  30. INFO 2022-04-10 01:03:02,026 : 接收响应 <<<: <Response [404]>
  31. INFO 2022-04-10 01:03:02,026 : -----调用完毕:todo_get_todo__todo_id__get-----
  32. INFO 2022-04-10 01:03:02,026 : 执行用例 todo_put_todo__todo_id__put
  33. INFO 2022-04-10 01:03:02,026 : -----调用接口:todo_put_todo__todo_id__put-----
  34. INFO 2022-04-10 01:03:02,026 : 发送请求>>> :请求数据 = {'todo_id': 1519, 'todoin': {'title': '', 'is_done': False}}
  35. INFO 2022-04-10 01:03:02,423 : 接收响应 <<<: <Response [404]>
  36. INFO 2022-04-10 01:03:02,423 : -----调用完毕:todo_put_todo__todo_id__put-----
  37. INFO 2022-04-10 01:03:02,423 : 执行用例 todo_delete_todo__todo_id__delete

鉴于篇幅有限,暂时介绍这么多,

文章首发与我的公众号测试开发研习社,如果你对这个话题依旧有兴趣,可以回复或评论:PBT

原创不易,欢迎关注,不错过技术干货,谢谢鼓励!

用 Python 为接口测试自动生成用例的更多相关文章

  1. tep集成mitmproxy录制流量自动生成用例

    使用 操作过程非常简单,流程如下: ①配置过滤域名 必须配置,不然会有很多无效流量造成数据冗余. ②启动代理 「示例」使用了反向代理,tep自带FastApi启动Mock服务: 「实际」使用正向代理, ...

  2. unittest框架扩展(自动生成用例)自动化-上

    一.思想: 基于数据驱动和代码驱动结合的自动化测试框架. 二.自动化测试框架步骤: 1.获取用例,用例格式:.ymal 2.调用接口 3.校验结果 4.发送测试报告 5.异常处理 6.日志模块 三.基 ...

  3. Python+Selenium学习--自动生成HTML测试报告

    前言 在脚本运行完成之后,除了在log.txt 文件看到运行日志外,我们更希望能生一张漂亮的测试报告来展示用例执行的结果.        HTMLTestRunner 是Python 标准库的unit ...

  4. Py福利,基于uiautomatorviewer 的Python 自动化代码自动生成工具分享(jar已发布GitHub,欢迎Star)

    前言做UI自动化无论你用SDK自带的uiautomatorviewer还是Macaca还是Appium自动的inspector,代码最多的就是那些繁琐重复的找元素后点击,输入,长按.....等.现在偷 ...

  5. [python] [转]如何自动生成和安装requirements.txt依赖

    [转]如何自动生成和安装requirements.txt依赖 在查看别人的Python项目时,经常会看到一个requirements.txt文件,里面记录了当前程序的所有依赖包及其精确版本号.这个文件 ...

  6. JMeter 接口测试 自动生成签名机制

    在进行接口测试时,遇到接口进行了签名校验,为实现自动生成签名,经过一点研究终于成功. 首先,需要从前端获取 签名加密包  XXXsign.jar..  建议将该jar包放在 jmeter lib 目录 ...

  7. python+selenium之自动生成excle,保存到指定的目录下

    进行之自动化测试,想把自动生成的excle保存到指定的目录下.网上百度的代码如下: import xlwt import time time = time.strftime ('%Y%m%d%H%M% ...

  8. appium_python 怎样实现参数化自动生成用例

    1.对于一种对同一个页面同一点 要用不同数据测试形成多条测试用例,如果复制的话 会让代码很冗长,并且并不好维护,现在用封装的方法把 不变的代码 和 变化的参数 分别封装,形成动态 生成测试用例 ,主要 ...

  9. [转]用Python做一个自动生成读表代码的小脚本

    写在开始(本片文章不是写给小白的,至少你应该知道一些常识!) 大家在Unity开发中,肯定会把一些数据放到配置文件中,尤其是大一点的项目,每次开发一个新功能的时候,都要重复的写那些读表代码.非常烦.来 ...

随机推荐

  1. 说说do...while和while的区别

    一.do-while语句 do-while语句的语法: do{ statement }while(expression); 看下面示例: var i=10: do{ i+=2: }while(i< ...

  2. 说出 5 条 IO 的最佳实践?

    IO 对 Java 应用的性能非常重要.理想情况下,你不应该在你应用的关键路径上 避免 IO 操作.下面是一些你应该遵循的 Java IO 最佳实践: a)使用有缓冲区的 IO 类,而不要单独读取字节 ...

  3. 指出在 spring aop 中 concern 和 cross-cutting concern 的不同之处?

    concern 是我们想要在应用程序的特定模块中定义的行为.它可以定义为我们想 要实现的功能. cross-cutting concern 是一个适用于整个应用的行为,这会影响整个应用程序. 例如,日 ...

  4. IOC 初始化源代码阅读之我见

    由于本人的能力有限,只能说出自己的见解,如有错漏什么的,请大家批评指出.由于代码封装太多,这里只列出了我认为的部分最重要的代码,一些简单的封装代码,不在下面列出.由于代码太过于复杂,在本次博客中,只列 ...

  5. springboot gateway 动态路由-01

    SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发 ...

  6. 学习saltstack (四)

    一.salt常用命令 salt 该命令执行salt的执行模块,通常在master端运行,也是我们最常用到的命令 salt [options] '<target>' <function ...

  7. 搞懂高并发性能指标:QPS、TPS、RT、吞吐量

      一.QPS,每秒查询 QPS:Queries Per Second意思是"每秒查询率",是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的 ...

  8. 用jq实现移动端滑动轮播以及定时轮播效果

    Html的代码: <div class="carousel_img"> <div class="car_img" style="ba ...

  9. Javascript--function的name属性

    1.非标准的name属性 function sayHi(){  console.log("Hi");} console.log(sayHi.name);

  10. Java/C++实现备忘录模式--撤销操作

    改进课堂上的"用户信息操作撤销"实例,使得系统可以实现多次撤销(可以使用HashMap.ArrayList等集合数据结构实现). 类图: Java代码: import java.u ...