写点什么

技术分享 | 如何确保 API 的稳定性与正确性?你只需要这一招

  • 2022-11-04
    北京
  • 本文字数:4798 字

    阅读完需:约 16 分钟

现在,越来越多的 Web 应用转向了 RESTful 的架构,很多产品和应用暴露给用户的往往就是一组 REST API,这样有一个好处,用户可以根据需要,调用不同的 API,整合出自己的应用出来。从这个角度来讲,Web 开发的成本会越来越低,人们不必再维护自己的信息孤岛,而是使用 REST API 这种组合模式。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0W05eiq-1667525481032)(upload://p9amtZb53gtaHCcb99ItvqPlfn3.jpeg)]


那么,作为 REST API 的提供者,如何确保 API 的稳定性与正确性呢?全面系统的测试是必不可少的。Java 程序员常常借助于 JUnit 来测试自己的 REST API,不,应该这样说,Java 程序员常常借助于 JUnit 来测试 REST API 的实现!从某种角度来说,这是一种“白盒测试”,Java 程序员清楚地知道正在测试的是哪个类、哪个方法,而不是从用户的角度出发,测试的是哪个 REST API。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AP8iFBVX-1667525481034)(upload://ndV1mRFdtMqwWB1DLkHCEEboJiT.jpeg)]


Rest-Assured 是一套由 Java 实现的 REST API 测试框架,它是一个轻量级的 REST API 客户端,可以直接编写代码向服务器端发起 HTTP 请求,并验证返回结果;它的语法非常简洁,是一种专为测试 REST API 而设计的 DSL。使用 Rest-Assured 测试 REST API,就和真正的用户使用 REST API 一样,只不过 Rest-Assured 让这一切变得自动化了。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMh5l1dp-1667525481035)(upload://gXHqRSbh2S4NpO0kxSHNtcmCz8p.jpeg)]


模拟 get 请求


雪球网是一个股票投资网站,你可以使用网站的搜索功能来查询股票信息,比如我们想查询 sougou 的信息,下 面利用了 charles 分析工具来查看请求和回答:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rwh8Jywb-1667525481035)(upload://rUclqzNdn42lva2hoFYuTcbowkI.png)]


这是一个 Get 请求,返回的内容格式如下:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgGF272C-1667525481036)(upload://tdknBCoswrf9rrN3qGZhdQ8b6il.png)]


现在,我们使用 Rest-Assured 来编写一个简单的测试程序调用相同的 Get 请求:


  • 第一步,我们要判断这是什么格式数据:json

  • 第二步,确定请求地址:从 charles 的结果中获取 y 为 https://xueqiu.com/stock/search.json


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNKd0uxz-1667525481037)(upload://m6PIthYsr55kQ78oMNt5UxDG2Fj.png)]


  • 第三步,填写表单:从 chrome 浏览器检查结果中查询 request 的 query 信息是 code:sougou


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PhfWkIZj-1667525481038)(upload://6bJ5a2aph0NxCwaEful7D1USYpY.png)]


我们的代码也很简单:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKHCW4Dn-1667525481039)(upload://dlHQwGSvNbwgxCwf87DW1vkQxVE.jpeg)]


返回的结果却很残酷:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CiiyByJI-1667525481040)(upload://stBYi4ds9v4wU5Z9DRRiPMcbbVk.png)]


与登陆账号,刷新页面有关的话,我首先想到了 cookie,网站都用 cookie 来保存账号相关信息,于是加入 cookie:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q9ELVS2f-1667525481041)(upload://A6ZOnGJepoDCGrrNjgwtZSXGzJD.jpeg)]


返回结果正确,你问我惊不惊喜,老实回答,不惊喜。因为我搞不明白为什么一个查询需要 Cookie 验证,如果不加 Cookie,返回的信息却是没有登陆!


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dr68qxfP-1667525481041)(upload://lhWAW6l2hhiuxsGEDSWejMdCUXu.jpeg)]


显然,我的 Cookie 并不包含登陆信息,因为我压根就没有登陆,当然这是网站的设计,与 Rest-assured 无关。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cyALledZ-1667525481042)(upload://4VzEQf1WzF0AewGbNBWqBQfXKwZ.jpeg)]更进一步


怎么区别 xml 与 json


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4c8MspL-1667525481043)(upload://9qon1wC97LGpv6hZRV7AcIVgJop.jpeg)]


答:你看就知道了嘛,xml 长这个样子


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S4qYQtuZ-1667525481044)(upload://xtDC8jCZ97WZtZKrl4YlIqPfAHb.png)]


json 长这个样子


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cPPS2nYn-1667525481046)(upload://yA5MQZ6zGqcKBV26cQNJ7BNTZIL.png)]


given,when,then 分别是什么


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qrJzzOF-1667525481047)(upload://kZSQ5f3EGLsDe1bD8FHS4Aku9u6.jpeg)]


答:given 用于放置需要的参数,比如上面例子中,我将访问参数:code 和 cookie 放到了 given 里;when 用于填 写要访问的 url;then 进行断言,来来判断结果是否正确。


模拟 post 请求


有的时候,我们想提交表单,这种情况下使用 get 会非常被动,于是 post 登场了。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ex11GVf0-1667525481048)(upload://nNkEJmMC1KrTEjMFt5KduIe7WKW.png)]


下面是代码。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OxP29mnw-1667525481049)(upload://2cFlPNKh2ItgVHWQJbFT64hjJdO.png)]


我相信此时你的内心是这样的。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIPIQJPp-1667525481050)(upload://vqleRCMoZ2LgBicK9wU12WoC2in.png)]


别着急,下面我会讲清楚...


在我大万维网世界中,TCP 就像汽车,我们用 TCP 来运输数据,它很可靠,从来不会发生丢件少件的现象。但是 如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,非常紧急的警车可能被前面的汽车拦堵在路上,整个交通系统一定会瘫痪。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ROFtWex-1667525481052)(upload://vn6f8VqpgyagQz18hMZrzDyKqay.jpeg)]


为了避免这种情况发生,交通规则 HTTP 诞生了。HTTP 给汽车运输设定了好几个服务类别,有 GET, POST, PUT, DELETE 等等,HTTP 规定,当执行 GET 请求的时候,要给汽车贴上 GET 的标签(设置 method 为 GET),而且要求把传送的数据放在车顶上(url 中)以方便记录。如果是 POST 请求,就要在车上贴上 POST 的标签,并把货物放在车厢里。当然,你也可以在 GET 的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在 POST 的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP 只是个行为准则,而 TCP 才是 GET 和 POST 怎么实现的基本。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kk5slMPW-1667525481053)(upload://mmMaH2NoZFL8Xa08Oliy7liwmyY.jpeg)]


使用断言


使用 EqualTo


在前面,我们使用了 EqualTo 判断值是否是“搜狗”:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X3KSrids-1667525481054)(upload://6OsUZDRt3n37trZMwHQKgn6R6Vg.png)]


它的作用显而易见:判断值是否相同。比如下面的例子


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OC8cnjwK-1667525481055)(upload://ulhnMI6Fwvn96ZVwpC67Hk5ERmq.png)]


如果你想验证 LottoId 是否等于 5,你可以这样做:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYswVckV-1667525481056)(upload://4Bru4nr10hjKCBuSxts7eTlJ2V9.png)]


使用 HasItems




你可以用再次 EqualTo(),对 WinnerId[0]用一次,对 WinnerId[1]用一次。



哈哈,当然不是。你可以使用 HasItems,它是这么使用的:



从根开始定位




额....请教王师傅。



比如下面的代码,我们可以这么验证:



使用 find





答对了,请一定要记住 xml 和 json 的区别,不要混谈,那么你能编写一个测试来验证杂货(groceries)的类别是 否包含巧克力(Chocolate)和咖啡(Coffe)吗?




这确实达到了我的要求,但代码明显有很多 bug,如果我更改了 category 的位置,像下面这样,你的代码就不 适用了,我不难为你了,请王师傅来解答吧:





find 的用法展示的很清楚,不需要我多讲,当然还有一点要注意,你可以这么使用 find:



**是个特殊用法,它从 xml 文档根部开始,进行深度搜索,直到找到符合我们需要的项。



使用 findAll



现在我手头只有 20 块钱,我只能买两本书,我更喜欢世纪的谚语和白鲸记,现在的任务是:挑选出格低于 10 的书籍,并且标题是“世纪的谚语(Sayings of the Century)”和“白鲸记(Moby Dick)”




对的,这时候应该使用 findAll,可以粗鲁的认为多个 find 的叠加。findAll 可以筛选出一批符合要求的数据,而 find 只能筛选出一个符合要求的数据,这就像是我们只能挑出一个人领取一等奖,但有很多人可以拿参与奖, 两个方法都有自己的用武之地。



下面的代码展示了 findAll 的用法:



提取想要的值


有时候,我们并不想验证是否正确,我们只想取出这个值以进行下一步处理,比如我想取出 next 的链接:/title?page=2,这种情况怎么办呢?



下面的代码判断内容是不是 JSON,并且标题是 My Title 的话,就返回 href 链接/title?page=2,这个值被存放在 nextTitleLink 中,以供我们以后使用。






当然,有两点需要注意:


  • 返回类型是 Response,我们可以用 Response.xxx 来二次提取想要的值。

  • extract().后面是 response()方法,不要写错了。


更改默认值


rest-assured 有很多默认值,也正因为如此,需要我们的填的参数可以很少,也可以很多,就像画画一样,可以很精致,也可以很简洁。



修改端口


rest-assured 发起请求时,默认使用的 host 为 localhost,端口为 8080,如果你想使用不同的端口,你可以这样做:



或者是这样



或者



修改 baseURI 和 basePath


你也可能改变默认的 baseURI、basePath



这就意味着,类似 get("/hello") 这样的一个请求,其实完整的请求为:http://myhost.com:80/resource/hello , 并且使用基础授权认证"username" and "password"。


其他


其他的默认值可以参考下面:



重置


你也可以重置为标准的 baseURL(localhost)、basePath(空)、标准端口 port(8080)、标准根路径 root path(" "),默 认的认证 scheme(none)以及 URL 编码(true),通过下面的方法重置:



specification


在不同的测试用例当中,我们可能会有重复的响应断言或者是请求参数,那么我们可以将重复的这一部分提取出来定义一个规范或者模板,这样的话在后续的测试用例当中就可以使用这个规范模板了。



为了达到这个效果,我们可以使用 RequestSpecBuilder 或 ResponseSpecBuilder 来实现,它们之间的区别 是,前者用在请求中,后者则用在 body 中。


ResponseSpecification 重用


例如,你想在多个测试用例中,都使用这样的断言:判断响应状态码是否为 200,并且 Json 数组"x.y"的大小是否 等于 2。你可以定义一个 ResponseSpecBuilder 来实现这个功能:



在这个例子中,需要重用的两个断言数据被定义在"responseSpec",并且与另外一个 body 断言合并,组成了这个测试用例中全部的断言,那么这个测试用例需要全部断言都通过用例结果才会通过,一旦其中一个断言失败,则测试用例的测试结果为失败。


RequestSpecification 重用


同样,假如你想在多个测试用例中重用请求数据,可以通过下面的代码来实现:



这里的请求数据被合并在"requestSpec"中,所以这个请求包含了两个参数("parameter1"和"parameter2")以及一 个头部("header1")。


总结


本文就 rest-assured 的基本功能进行举例说明,其中例子大多来自于官方文档,同时我也建议大家多去阅读该开 发文档,其中有很多我们没有讲到的东西。


点击下方链接免费领取:性能测试+接口测试+自动化测试+测试开发+测试用例+简历模板+测试文档

https://note.youdao.com/s/HtxtCnaf

用户头像

社区:ceshiren.com 微信:ceshiren2021 2019-10-23 加入

微信公众号:霍格沃兹测试开发 提供性能测试、自动化测试、测试开发等资料,实时更新一线互联网大厂测试岗位内推需求,共享测试行业动态及资讯,更可零距离接触众多业内大佬。

评论

发布
暂无评论
技术分享 | 如何确保API 的稳定性与正确性?你只需要这一招_霍格沃兹测试开发学社_InfoQ写作社区