年前搞过一段时间的接口自动化测试,从无到有到落地执行,基于 requests 的封装调用,感兴趣可查看历史文章。
为什么不用现有的开源测试工具呢?
如 Jmeter、Postman 等:
1. 公司从提测到出包有一套完整的自动化流程,使用 Jenkins 可以将自动化测试项目接入到流程内,出包就运行自动化测试,保证后端环境的连通性和可测性;
2. 现有开源工具不易定制和扩展(a. 个人技术达不到; b. 框架部分功能不支持);
3. 自己写项目,可以更好的理解代码逻辑和实现原理,对自身成长有很大帮助。
基于之前的 API 测试,总结了使用过程中遇到的痛点:
-
不灵活:编写 unittest 风格的测试用例,需要固定格式,如:必须继承 unittest.TestCase、固定前置后置 setup / teardown 等;
-
冗余多:
-
前置:基本一致,都需要调用获取配置数据、公共参数等函数;
-
断言:逐个字段校验,密密麻麻的 assert 看着头疼,字段变动难以维护;
-
-
未加入日志记录功能:之前封装的 request 和断言函数,每次运行完成后仅有 HTML 报告展示,快速定位问题时,一份详细的 log 日志更为高效;
-
无法进行参数化:比如一条用例,不同的传参,返回不同的结果。就需要增加用例修改下传参,相当于用例写两遍,这是个问题!同时,请求完成后,仅断言预期改变的响应值还是全部值都断言?这又是个问题;
-
用例失败无重跑机制:需要在用例内强制设置运行次数,对有一定失败率的用例来说,不太友好;
-
运行模块化用例需要定制 test suite 进行组装。
针对以上问题,发现部分问题在 pytest 上都得到了很好的解决:
-
pytest 可以运行 unittest 风格的测试用例,兼容性好,用例编写规则简单,仍然需以 test 开头或结尾;
-
灵活的 fixture 和 conftest.py 机制,二者结合内完美解决用例文件内前置冗余的问题;
-
参数化运行用例,@pytest.mark.parametrize 数据驱动;
-
失败重试机制:@pytest.mark.flaky 不用再强制设置用例运行次数,还可以 通过 @pytest-rerunfailures 设置重跑所有失败用例;
-
通过 pytest.main() 参数来指定运行的测试用例,可以指定文件、模块、类、函数;
-
丰富的插件机制和支持 allure 报告,也是亮点。
还有其他问题,比如:
1. 日志记录功能:刚开始写用例的时候是没考虑到的,觉得仅有报告展示就足够了。后来遇到紧急需求,需要通过同一个 API 去请求不同书籍的价格,测试完成生成的 HTML 报告,定位问题总得打开浏览器去报告内查找,不如日志 Ctrl + F 来得高效。
解决办法:重新封装 logging、requests、assert ,将请求、断言信息写入 log 文件内,以天为单位保存起来,基本满足需求,如下图所示。
2. 断言冗余:上面参数化只解决了用例冗余的问题,接口返回值多的情况下,逐个字段校验依然需要大量的 assert,参考了 httprunner yaml 文件写用例的实现方法,创建断言器,通过获取断言器的数据,根据数据进行校验,其实也是相当于做了用例断言的参数化。
问题解决后,发现还可以继续优化:
-
使用的是 pytest 框架,既然是框架,就肯定有固定的格式、用例模板,所以每次写 case,还需要手动去创建文件,重复的写入 import pytest 、import allure 、class xxx 等等等巴拉巴拉,那就还有优化的空间,写一个简单的 case 模板,自动去根据 API 名称生成用例文件,架子有了,工作重心就放在用例设计上了,还可以将所有的接口名称根据模块划分好,直接自动化生成全部用例,每条用例里面再根据业务详细修改;
-
刚才提到的断言参数化,因为这个断言器是固定的数据格式,那断言的时候还是需要手工的去写入到用例文件内,优化优化~ 目前我的实现思路就是:先判断当前用例内是否存在断言器,如果存在断言器,则不创建;如果没有,则发送请求后根据返回值自动生成断言器写入到用例文件内,这次请求的返回值就作为预期保存下来,作为下一次断言的预期,前提是请求成功哈。
另外,研究 httprunner 发现 har2case 这个文件,功能是从 charles 导出 *.har 文件,解析 har 文件后生成一份 yml/json 文件。正好我的项目还有个弊端,当 post 请求含有 data-form 数据时,需要手动写入该表单的数据到用例内,正好这个功能我可以用上,稍微修改了下,从 charles 导出为 *.har 文件,根据 *.har 文件将解析出来的 data-form 数据,自动填入 case,算是得到了解决。其实断言器也可以根据这种方法生成,但是我懒得每个接口都去 charles 导出一遍。
最后,自动生成的用例就长这个样子:
生成的 allure 报告长这个样子: