自 18 年起,一直在思考如何把自动化测试框架转化为测试平台,让自动化测试告别代码。中间搁置两年,20 年底于上家单位尝试在做,最后效果还算不错,但是很多设计在使用中发现仍不完善,因此后来下定决心自己一个人重做一版。
通常来说,设计自动化测试框架时模块大概可以拆分为:测试对象(API/UI)、测试用例、测试数据、测试环境、测试计划、测试日志、测试报告以及一些辅助功能如公共参数、测试文件等。那么我们将其平台化后,也可以参考这个思路来做,但是有些功能需要做适当的变更:
-
首先是测试用例和测试数据,常规的测试框架遵循 POM 设计模式,用例代码和数据是分离的,从而方便管理。平台化后,理论上也该如此,最早我也是这么做的,采用用例模板和用例数据分离的模式,但最终发现写用例时极为不便,而且也有各种各样的问题。其实平台化后,没有必要再做用例和数据分离,框架做分离是为了数据管理更加直观,不受代码影响,更加容易维护,但是平台化后,代码几乎为 0,用例采用配置化来写,因此用例步骤与数据结合在一起维护反而更加直观。 -
其次,平台化后虽然不用写代码,但也会带来很多局限性,一些特殊的处理不易实现。比如说参数加密,框架只需要写一个公共加密函数,用例步骤中调用。平台化后,要想支持这一点,那么就应该有函数管理功能,支持统一维护函数,并在用例调用。函数管理应该有常用的内置函数,同时也要支持自定义。 -
还有就是 UI 自动化的实现,我们可以将驱动代码翻译成关键字,其实 UI 测试脚本每一步无非就是包含三个内容:操作动作、页面元素、输入数据。平台化,可以这些步骤封装成一个个操作控件,并赋予其言简意赅的关键字,同时页面元素也可以统一在一个地方维护,方便管理。写用例时通过选择关键字,带出其所需的元素和数据,就能完成一步步的 UI 测试。此外,操作控件也应该支持自定义,毕竟总归有一些特殊使用。 -
最后就是测试执行,为了避免执行资源、网络限制等问题,分布式执行、可视化调试、跨环境执行很重要。这样就需要把测试执行独立在平台之外,封装成一个单独的项目,我称之为测试引擎。测试引擎既可以部署在服务器上,也可以在办公电脑上启动,与平台的通信通过 http 接口,这样就可以解决诸多问题,后面我会详细介绍。
接下来,我按照自己已实现的平台来详细介绍一些关键的功能点设计:
一、测试对象管理
自动化测试包括了 API 和 UI 以及 APP,其中 UI 和 APP 的测试对象都是页面元素,只不过一个是 Web 页面元素,一个是 Activity 页面元素。所以测试对象管理,可以分为接口管理和元素管理。无论哪种管理,一定要支持模块化管理,这样才能逻辑清晰和维护方便。因此我们可以采用常见的左树右表来进行管理,接口管理列表如图所示(元素同理):
接口管理相对于元素管理较为复杂,元素管理只需要维护其名称、定位方式即可。但是接口需要维护的数据很多,这里我们可以参考 postman 的接口请求,除了接口基本信息外,可以将其请求信息按照 TAB 展示,如图所示:
二、测试用例管理
测试用例的管理和对象管理一样,也应该按照模块化管理。其次,API 用例和 UI 用例的管理和使用是一样的,没有必要分开管理,只是写用例的模板不一样而已。但用例编写是平台化的重中之重,需要注意的地方有很多 (用例细节 DEMO 图片太多,我就不贴了):
- 测试数据与环境的解耦 我认为一个合格的测试用例不应该和环境耦合,这也是我没有再做测试用例和测试数据分离的主要原因。一个合格的测试用例应该是数据可回收的,即便是那种复杂流程的用例,也应该是一个用例集合的数据是可回收的。因此测试用例不会关联测试环境,环境只是作为标签告诉使用者,这个测试用例适用于哪些环境,方便执行时选择环境。
- 测试对象如何关联环境 测试用例不关联环境后,那么 API 测试的请求域名和 UI 测试打开的页面域名如何一个个环境关联上呢。在我的设计中通过两种方式即可完美覆盖,一是路由匹配,通常来说在多个微服务域名的环境中,每个接口属于那个微服务通常都会以接口地址的首位路由来识别。其次,如果这种方式不适用,那么我们可以对每个接口设置域名标识,每个环境也维护相关标识的域名,强关联后,无论如何也不会出错。
- 断言和提取参数如何做 ①API 测试是很简单的,参考 jmeter 即可。正常接口返回都是 json 数据,那么只需要通过 jsonpath 表达式即可提取响应值,并对其做断言和参数提取即可。如何是其他响应数据,也可以通过使用正则表达式进行提取。 ②UI 测试也不困难,UI 断言和提取的对象无外乎就是元素是否存在、元素文本、元素坐标等等相关信息。因此我们可以将断言和关联作为操作步骤来维护,写用例时直接使用即可,道理和普通操作动作一样。
- 动态变量的提取和使用 常见的动态变量有随机函数、关联变量、公共参数,我们可以通过占位符方式来标识使用,比如说{{@func}}表示函数,{{$name}}表示公参, {{name}}表示关联参数,并且可以混合使用。比如说提取的关联参数需要进一步处理,那么我们可以写个自定义函数,并将关联参数作为入参,最终得到结果,如{{@func({{name}})}}。这些占位符在用例执行前会被测试引擎里渲染成实际的值,并最终使用。此外,测试用例的提取参数应该有个作用域,不应该只是作用于当前用例,遇到一些流程用例可能更希望用于一整个测试流程,因此可以引入测试集合的概念,一个测试集合内的用例是串行的,从上而下执行,提取的参数也会在整个测试集合内被使用。
- 一些附加的逻辑控件 接口测试多少需要一些逻辑控制,比如说接口之间公用 session、接口请求成功后设置静默时间等。此外,为了避免仍有一些无法适用的测试场景,可以参考 jmeter 设置 beanshell 等。但这多少会带来管理困难,所以我也还没做。
当然还有很多其他小细节,这个一一叙述过于占用篇幅,因此就不再继续赘述了。
三、测试集合和测试计划 测试集合这一层的作用有两点,一是如二中所说,为了用例串行和动态变量作用域的设计,二是测试集合可以理解为一个模块,将用例按照模块来执行,能更好的分析结果。 测试计划就是最终产出测试报告的用例集。执行支持定时、支持手动、支持外部调用 (集成到 CI/CD)。测试计划可以按照测试集合的维度来并发执行,如此就可以提升测试效率。 简而言之,用例执行时需要考虑串行和并行,因而设计成两个层级。
四、测试报告 平台化后的测试报告其实与测试框架的报告差不多,都是按照模块化出结果,并且展示用例执行过程中的详细日志,UI 用例还要显示截图。报告页面无须多花里胡哨,能清晰看到失败和错误,方便分析即可,如图所示:
五、测试引擎 测试引擎是测试平台的核心,负责测试平台的执行。一个平台可以注册多个引擎,每个引擎由独立的 code 和秘钥,当引擎被启动时,向平台每分钟发送心跳,平台接收后则默认引擎可用状态为在线,超过三分钟没有收到心跳监控,设置为离线不可用。其次引擎有获取任务接口,拿到属于自己的执行任务。也有任务终止接口,避免一些用例卡死引擎,引擎拥有者可以自己选择关闭引擎当前执行任务。
引擎执行的原理其实很简单,使用 python 的 unittest 作为核心来管理用例执行,并对其做一些二开。其次当用例开始执行时,先会对用例进行渲染,将动态变量渲染成最终值,完成渲染后才会进行最终的执行。最终执行的驱动,接口用例使用 request、UI 用例使用 selenium。
简而言之,引擎其实就是在原有的测试框架上包一层平台通信,使用很简单。附上我做的测试引擎代码:测试引擎 GIT 地址
主要功能设计就是这些,还有些细枝末节下次补充。小伙伴们有兴趣可以试试看我发布的 DEMO,因为测试执行会发邮件的,避免我的邮箱爆炸,所以先不提供测试账户,可以手机号注册用户,账号仅作试用体验,不作他用。平台 DEMO 地址
福利 :关注公众号获取
|