看过一些帖子,觉得搞技术要有自己的一些想法。
这个功能本身很好,但更适用于控制层的测试(稍后详解),对于接口测试觉得意义不是很大,原因如下:
关于控制层测试,也就是分层测试中的 web 层测试(B\S 的前端测试、app 中的 h5 测试),就是把前端测试再次细分,隔离 UI 展现,把传统的前端脚本中的操作转化为页面请求的操作(可理解成未渲染的页面),并对之校验。以 Java 技术栈开发的产品为例,无论接口测试还是 web 测试,最终交互的还是 servlet。所以个人觉得,自动生成 HAR 文件,更适合于控制层的测试,至于生成的格式是否必须用 HAR 标准,视具体情况而定吧。
以上纯属个人观点,不代表客观性。
估计是我最早在接口测试中提到这个根据 Har 生成接口测试的功能. 在我提之前, Gatling 和其他的性能测试也已经支持通过 Har 生成各种类型的测试脚本了. 其他的 LR 等工具本身也是借助于已有的产品交互来截获数据, 本质也是一种事后补充的测试用例生成方法.
是不是 Har 不重要, 比如 Nginx 的带 post 的 log 也可以. tcpdump 的数据也可以. 我还真的调研过可行性, 的确有工具能做到.
只要能抓到你测试需要的数据即可. 在可行的格式里面 Har 是最通用最简单的标准.
我选择 Har 是因为各种代理工具普遍都支持 Har, 这样自己的接口测试框架也容易跟 fiddler charles 等代理工具配合使用.
理想情况下是接口规范是先出, 然后编写测试用例. 但是实际的情况是. 大部分的公司都是后补的规范和文档. 另外就是很多公司在推崇小迭代. 或者是为现有的系统补充测试用例保证回归. 所以大部分的接口测试都是后补的. Har 等方式就能带来实际效果.
所以你的前两条假设和约定都不成立. 接口测试前置是比较好的, 但是这个理论不符合实践.
往长远看 Swagger 标准才是真的提出了接口测试前置的可行性方案. 在 api 设计的时候它能自动生成接口规范文档和接口交互模拟测试环境. 甚至是接口测试用例. 这个会是未来. 但是在目前更通用而简单的方案还是录制生成修改的方法
根据接口测试的经验, 是否前置, 是否足够简单也不重要, 重要的是维护成本. 如果能在极短的时间和极低的成本实现接口测试的编写和运行. 就足够好了. 比如有了自动生成接口测试用例的功能, 接口测试框架自身是否足够简单已经不重要了. 这也是 gatling 和 lr 工具的共同设计思路.
往大了说, 接口测试并不是描述的是 Http 接口, 他是一个广泛的接口, 不仅仅是某一层的网络接口协议, 还包括其他的"接口",
比如对 servlet 的调用, 对 class method 和 api 的调用, android activity 的调用, android message 的消息传递. 对 gps 的信号调用, 这些也都是接口. 找到一个可以方便定制和分析的接口层和接口维度都可以做接口测试. 我记得社区里面两年前还有人提过用 am 构造 intent 来测试 activity. 这也是一种接口测试思路. 属于广义接口测试. 在单测之上还有好几层概念可以实践. 其他公司有在探索. 不过就目前来看. 对大多数的公司来说, http 的接口测试是行业最成熟最标准的实践了.
#1 楼 @seveniruby 如果是由于流程问题没标准文档,需要事后补充,这个我觉得可以,但是通过抓包(应该还要开发提供一个接口地址列表,进行过滤吧)生成的接口案例,测试角度来讲,总感觉缺少了点权威性,毕竟抓包抓的是开发写的代码发出的请求和响应,如果开发代码本身就是有 bug 的,何解?也就是说开发的代码不能既是球员又是裁判。
然后 Gatling 和 LR 主要是从性能测试的角度设计的,是在假设程序的功能正确的前提下考虑的,其自动生成的脚本也是如我所说的模拟用户操作的请求流,也就是 V 和 C 的交互,当然 LR 支持多协议,只要是符合其协议的接口也能测试。然而这些工具的设计初衷是性能测试为主,是在假设程序或接口正确的前提下进行录制的。
#2 楼 @quqing 接口的正确性需要多方评估. 但是实际中这样的成本较大, 经验丰富的测试工程师只需要 review 下接口的交互即可. 分析 Har 的调用关系, 跟分析代码的调用关系类似. 都是一种 review 机制. 通过这个即可保证. 常常是在测试过程中, 所有人都觉得没问题, 而测试工程师通过抓取接口就能分析出一些潜在的隐患. 这个过程中懂接口正确性的其实只有研发和测试, 产品帮不上忙的.
开发的代码有 bug 会发生发错 不发 多发 解析错等各种情况. 无论有无 bug, 我都会分析整个流程是不是出现了非合理现象, 进行评估.
也就是一个 feature 的交互流程里从输入触发开始, 到接口发出, 数据收回, 本地处理, UI 渲染等整个流程都会被审核.
以至于现在我们组就算不做接口测试, 也会习惯的挂上拦截代理去分析整个交互流程. 确保每个环节都是对的.
code review 本质是 review 了代码的流程和规范. Har 代表的是接口的流程. 所以理论上讲, 这块应该也有个接口流程的 review. 文档不靠谱, 规范也不靠谱. 就算他们都正确最后落地的实际还是可能有 bug 的代码. 在度量这个流程的正确性中, 貌似也只有 Har 完整保存了整个接口交互的证据.
Har 只是 http 协议的典型代表, 任何印证接口流程的数据皆可代表, 比如 api 中的 send receive log. controller 和 servlet 的调用链记录, 代理工具, har, fiddler 导出的 curl 命令序列等.
我也好奇的是, 为什么行业里面对 Har 这么重要的东西视而不见. 据我所知, 在任何正规的教科书中都不曾出现过这个概念.
#3 楼 @seveniruby “以至于现在我们组就算不做接口测试, 也会习惯的挂上拦截代理去分析整个交互流程. 确保每个环节都是对的”,这点我非常赞同,我之前也写过分层框架实现该功能了,优点多多。
HAR 是个标准,对于通用化确实非常好,特别是对于打算开源的工具。但实际应用中,可能有些场景需求其无法满足,可能有的人自定义一套内部标准,只要实现解析这套标准的方法即可。我之前也写过一套自定义的标准,是结合分层思想的,把部分内容裁剪掉了,也增加了部分内容,以下是 demo:
{
"testcase": "layTestCase",
"scope": 7,
"dbDependController": true,
"uiDependController": true,
"useProxy": true,
"controller": [
{
"step": 1,
"url": "{internetAddr}inProduct/product.screen",
"method": "get",
"param": "productId=${productId}",
"postBody": false,
"encode": "GBK",
"proxy": "xx.xx.xxx.xx:8080",
"delay": 0,
"isDownload": "buy1"
},
{
"step": 2,
"url": "{internetAddr}login.do",
"method": "get",
"param": "date=#{UTC}",
"postBody": false,
"checkPoint": "\"code\":\"1\"",
"encode": "GBK",
"delay": 0,
"proxy": "xx.xx.xxx.xx:8080"
},
{
"step": 3,
"url": "{internetAddr}insProduct/saveChoices.do",
"method": "post",
"param": "totalPsn=2&childPsn=1&productPeriod=12&startDate=#{getDate(1)},
"postBody": false,
"regx": "idProduct={input type=\"hidden\" id=\"idProduct\" name=\"idProduct\" value=\"(.*)\"/>}#1,
"encode": "GBK",
"delay": 0,
"proxy": "xx.xx.xxx.xx:8080",
"isDownload": "buy2"
},
{
"step": 4,
"url": "{internetAddr}insuranceProduct/checkProdcut.do",
"method": "post",
"param": "idOrder=${idOrder}&idProduct=${idProduct}",
"postBody": false,
"encode": "GBK",
"delay": 0,
"proxy": "xx.xx.xxx.xx:8080"
}
],
"database": [
{
"refStep": 3,
"mode": "sql",
"dbName": "dbName1",
"queryParam": "${idOrder},${idProduct}",
"sql": "select id,amount from table where id =? and product=?",
"compareType": "content",
"dbAssert": "ID_PRODUCT=P004,AMOUNT=1"
},
{
"refStep": 4,
"mode": "proc",
"dbName": "dbName2",
"queryParam": "iiiooo,${idProduct},#{getDate(1)},2012-05-05,null,null,null",
"sql": "gs_package_calc.calc_sus_day",
"compareType": "content",
"dbAssert": "a,3,null"
}
],
"ui": [
{
"refStep": 1,
"chkImages": "tzlc,perINfo"
}
]
}