demo 地址
https://github.com/BitingWind/scabbard
背景
广告交易后台是一个基于多用户多角色多权限的系统,底层数据链路是打通的,因此需要一个整体的权限+数据打通的自动化解决方案,来覆盖主流程场景接口覆盖以及权限校验。
问题:
部门项目开始到目前为止,后端的代码结构,接口变动非常大,因此需要高度的抽象与封装才可以抵御后端接口变动而不至于影响已有场景自动化case的风险
数据复杂度随业务增长不断变高,直接影响了问题定位,测试效率
明显的收益点:
接口抽象,请求数据构造抽象,审批流抽象,与场景自动化case解耦,可以低成本改造来适配后端接口重构与改造
真实可用的测试数据的构造,升级测试习惯为脚本+手工跟进新功能测试,提升测试落地效率。
日常使用场景
核心场景case回归
- 持续集成自动触发,提测,上线前回归
- Jenkins job 外部触发使用
测试场景数据创建维护
- 本系统批量数据构造,配合手工测试,支持RD、PM测试
- 上下游需求特定数据构造
框架以及分层实现
当前方案:
HTMLTestRunner + python 2.7 + unittest
分层:
总共分为4层,从下至上依次是
Framework layer
Project abstraction layer
Testcase layer
Testsuite and Runner layer
1 Framework layer
承载的功能是对原生或第三方module封装,基本组件的封装,各种utility比如lark发消息,生成随机数,获取list随机内容,生成随机红包list等。
MyRequests 模块
背景:广告交易后台是多用户多角色使用的系统,并且介入了安全校验,需要在API请求中特殊处理。
本模块使用了requests module并且进行替换封装,支持特殊的登录校验
这里需要跟业务系统的代码达成一致,在cookie中特殊赋值,绕过非必要的校验
DB utils模块
提供sqlalchemy 的make engine和 get db session的方法,为Project abstraction 层提供基本DB链接服务;具体的项目目录,会去单独实例化DB链接,并在ops方法里面实现与db的交互。
业务变化快的,可以直接使用sql取值,比较稳定的推荐model方式
RPC Client模块
目前采用的方案,底层是公司统一的服务管理平台的接口,在init的时候,配置单个项目所需要的 RPC 地址等内容,在方法调用上类似 RPC 方法调用,形式跟API一样。
Utils 模块
可复用的公共模块,会抽象到这里。几个典型应用为:
CommonDataUtil:构造常用随机数字,随机邮箱,电话,随机金额,拆分list,获取list自己等,支撑fuzz test
DateTimeUtil: 处理日期以及 时间相关数据格式化以及构造。
CollectionUtil: 处理list等
LogUtil: 日志格式化输出工具,可支持 json 以及 dict 格式化
IMUtil:封装公司内部IM发送方法
MQUtil:实例化MQ发送者,mock消息发送
HTMLTestRunner模块(原生)
第三方的将unittest test.suite进行html转换,统计以及展示自动化结果
触发的入口在根目录test_runner目录里面,各个项目有各自的main函数,来触发unittest并且返回testsuite结果,放入htmlRunner打印
2 Project Abstraction Layer
项目抽象层主要用于对于测试跟进的项目进行抽象与封装,主要目标有三个:
后端接口封装,返回业务测试关注的字段
后端接口构造数据的方法封装,对标测试关注的业务场景作为入参,快速准备接口payload,且融入大量模糊与随机数据作为Fuzz test 支撑
DB方法封装,针对常用的查询,对标测试用例使用场景进行封装,便于复用
从而做到可以对于后端接口的变化甚至重构,都有很强的抗风险能力,降低Case和代码的修改成本。
不同项目会被隔离在不同目录下,互相的接口,DB,请求数据构造方法相互独立,测试数据构造方法又可以共用,可以支撑跨项目的数据构造
业务常量封装
状态枚举,期望的正确校验数据等
DB Ops封装
自动化中使用到的DB交互方法,或者查询,会封装到这里,方便复用
Framework layer中, DB utils里面已经有提及,可参考'DB utils模块'章节
接口Ops封装
后端接口,按照业务分支,拆分封装,hostname,url抽象去除,把payload构造抽象出去,不影响测试业务非核心字段增加随机属性,返回结果封装关键期望字段。
收益点:对于当前单独业务分支需求变更,接口变更,修改部分会更加聚焦
Post请求数据封装(template)
后端Post请求构造是最复杂的部分,尤其在底层数据有互相耦合的交易平台,需要使用真实的上游数据
因此,Post请求数据,会把影响业务测试流程的变量当做入参,其他无关流程的变量随机化,进行payload数据的构造。
构造方法抽象的非常大的优势,就是可以自如的应对后端代码重构而降低测试代码修改动作,比如后端的代码部分被重构过,那么其他耦合部分增加的,只需要修改对应的方法,上层就不用动了
底层util类里面会有大量随机构造的方法可以被调用,增强payload信息里面长短,数据类型构造的随机性与复杂性,可以随着自动化频繁跑,增强测试数据的复杂维度,在前端和后端的边界处理上,是可以发现一些边界bug的。
通过结合业务常量封装,代码里面,减少枚举的出现,替换为中文,增强代码可读性以及后续测试代码可维护性
3 Testcase layer
Testcase layer 包含 测试数据mock准备(test_data_prepare)和 测试cases(test base classes)
测试数据准备
此部分做为对 Post 请求的二次封装 或工具类,会生成一些符合特定场景的测试数据。
主要目的 有两点:
自动化case 的高度集成(涉及后端代码改动时,case的修改成本大大降低)
方便快速造数(为下游或业务需求造数收益较大)
自动化测试cases
- 此模块继承unittest.TestCase,依据不同业务场景划分\以class 为单位,条件覆盖 view层场景cases,对接口返回接口结果、DB存储结果进行校验assert。对于通用的校验,可以进一步抽象,统一调用一个业务验证的方法。
覆盖好全量的组合,快速平铺,且保证场景独立于隔离
4 Testsuite and Runner layer
本模块包含各个系统的测试case集合 和 测试集合执行。测试集合分为 主流程 case集合(sanity_suite)。职责在于 根据不同项目对case进行组合,分级,配置上层参数,控制执行组合。
Sanity_suite
此测试集合主要是筛选系统中的20%的case 集合,以保证覆盖80% 的主要流程。方便以快速的时间进行回归覆盖。主要用于持续继承中的自动化case执行。
筛选方法:
# 加载单个
suiteTest.addTest(TestClass("test_func_case"))
# 加载整个类
suiteTest.addTest(unittest.TestLoader().loadTestsFromTestCase(TestClass))
# 加载整个路径下,符合test运行条件的所有类
suiteTest.addTest(unittest.TestLoader().discover(path))
Test_Runner
各模块继承base_runner,执行自动化case集合。 Test runner 可以通过解析 base_args内容,覆盖当前runner 的基本参数,比如 发送结果通知的参数,还可以加载更多自定义参数。
包含case执行、测试报告输出、IM通知、个性化自定义等
之前提到的执行时间问题,可以在这一层做并发处理。
触发与集成至CI
Jenkins Job 执行自动化
自动化会被一个通用的自动化触发job封装
大致流程为:
Git拉去API_AUTOMATION_FRAMEWORK最新的代码之后,根据传入的项目名称,需要流量打入的ip端口,以及main函数的名称,命令行在某个测试机器,触发自动化。
自动化代码会获取CI传入的ip端口,会替换到代码里面有的测试环境ip端口,完成测试环境指定
执行集成触发
如果与CI集成,那么只需要CI的job包裹当前的自动化触发的job即可。
外部HTTP 触发
触发url POST请求
https://jenkins.domain.com/jenkins/job/Auto_Job/buildWithParameters
参数
# jenkins 手工配置
token:xxxx
# job 对应的参数
args*:values*
# 查询获取 last build id
https://jenkins.domain.com/jenkins/job/Auto_Job/lastBuild/api/
# 根据build id 获取具体构建状态 get
https://jenkins.domain.com/jenkins/job/Auto_Job/${build_id}/api/json