如何使用 Loadgen 来简化 HTTP API 请求的集成测试
引言
在编写 HTTP 服务的过程中,集成测试 [1] 是保证程序正确性的重要一环,如下图所示,其基本的流程就是不断向服务发起请求然后校验响应的状态和数据等:
为大量的 API 和用例编写测试是一件繁琐的工作,而 Loadgen [2] 正是为了简化这一过程而设计的。
一个简单的测试
假定我们在 127.0.0.1:9100 端口监听了一个 Pizza [3] 服务,现在我们通过如下配置来测试集合(collection)的创建:
# loadgen.yml
requests:
- request:
method: PUT
url: http://127.0.0.1:9100/test_create_document
然后运行 loadgen -config loadgen.yml:
$ loadgen -config loadgen.yml
__ ___ _ ___ ___ __ __
/ / /___\/_\ / \/ _ \ /__\/\ \ \
/ / // ///_\\ / /\ / /_\//_\ / \/ /
/ /__/ \_// _ \/ /_// /_\\//__/ /\ /
\____|___/\_/ \_/___,'\____/\__/\_\ \/
[LOADGEN] A http load generator and testing suite.
[INF] warmup started
[INF] loadgen is up and running now.
[INF] [PUT] http://127.0.0.1:9100/test_create_document -
[INF] status: 200, error: <nil>, response: {"success":true,"collection":"test_create_document"}
[INF] warmup finished
...
为了便于阅读,笔者对程序输出进行了简化,实际会略有区别
可以看到,Loadgen 实际上帮我们做了类似这样的操作:
curl -XPUT http://127.0.0.1:9100/test_create_document
一些简单的测试
上述示例中我们只测试了创建单个集合,但是实际情况下短时间内会有许多请求涌入,对于创建大量的集合我们又该如何测试呢?
这里就需要用到变量 [4] 的概念:
# loadgen.yml
variables:
- name: id
type: sequence
requests:
- request:
method: PUT
url: http://127.0.0.1:9100/test_create_document_$[[id]]
上述配置中,我们定义了一个名为 id 的变量,sequence 是一个特殊的类型——每次被读取时它的值会递增,因此 Loadgen 会不断发起类似这样的请求:
curl -XPUT http://127.0.0.1:9100/test_create_document_0
curl -XPUT http://127.0.0.1:9100/test_create_document_1
curl -XPUT http://127.0.0.1:9100/test_create_document_2
...
在 Pizza 的日志中也记录了这些请求:
$ pizza
___ _____ __________ _
/ _ \\_ \/ _ / _ / /_\
/ /_)/ / /\/\// /\// / //_\\
/ ___/\/ /_ / //\/ //\/ _ \
\/ \____/ /____/____/\_/ \_/
[PIZZA] The Next-Gen Real-Time Hybrid Search & AI-Native Innovation Engine.
[INFO] Collection test_create_document_0 created
[INFO] Collection test_create_document_1 created
[INFO] Collection test_create_document_2 created
...
不那么简单的测试
目前为止,我们只是不断的向一个服务“塞”大量的请求,但比起发起请求,我们常常更关心程序的响应是否符合预期,也就是说,响应需要满足我们定义的一些条件,这可以通过 Loadgen 提供的 断言 [5] 功能来实现:
# loadgen.yml
variables:
- name: id
type: sequence
runner:
# 检查返回值是否正常
assert_error: true
# 检查断言是否通过
assert_invalid: true
requests:
- request:
method: PUT
url: http://127.0.0.1:9100/test_create_document_$[[id]]
assert:
equals:
# 注意,这里我们故意设置了一个“不正常”的值,以迫使断言失败
_ctx.response.body_json.success: false
在上述配置中,我们启用了 Loadgen 的检查,然后定义了一个会失败的断言:
equals会校验给定路径_ctx.response.body_json.success是否与期望值false相等_ctx.response.body_json表示 JSON 格式的响应体success表示响应体中该字段对应的值,可以用path.to.nested.key来访问嵌套的字段
也就是说,给定响应体 {"success":true,"collection":"test_create_document"},Loadgen 会检查 success 的值是否为 false:
$ loadgen -debug -r 1 -d 3 -config loadgen.yml
#0 request, PUT http://127.0.0.1:9100/test_create_document_$[[id]], assertion failed, skiping subsequent requests
[WRN] '_ctx.response.body_json.success' is not equal to expected value: true
#0 request, PUT http://127.0.0.1:9100/test_create_document_$[[id]], assertion failed, skiping subsequent requests
[WRN] '_ctx.response.body_json.success' is not equal to expected value: true
#0 request, PUT http://127.0.0.1:9100/test_create_document_$[[id]], assertion failed, skiping subsequent requests
[WRN] '_ctx.response.body_json.success' is not equal to expected value: true
#0 request, PUT http://127.0.0.1:9100/test_create_document_$[[id]], assertion failed, skiping subsequent requests
[WRN] '_ctx.response.body_json.success' is not equal to expected value: true
上述命令我们使用了:
-debug启用更详细的报错-r 1 -d 3减少发起的请求数(1req/s持续3s)还有一个需要注意的细节是
... is not equal to expected value: true,这里报告的是success字段实际的值,而不是断言中定义的期望值。
可以看到,Loadgen 每次请求的断言都失败了,不过我们可以通过日志来快速定位出错的原因以便于调试。
更进一步的测试
现在我们创建了大量的空集合,是时候向其中添加一些文档(document)了,但是,一个首要解决的问题是,每次测试创建的集合名称是带有 $[[id]] 这个变量的,我们如何知道应该向哪个集合上传数据呢?一个可靠的解决方案是借助 Loadgen 的寄存器 [6] 功能:
# loadgen.yml
variables:
- name: id
type: sequence
runner:
assert_error: true
assert_invalid: true
requests:
- request:
method: PUT
url: http://127.0.0.1:9100/test_create_document_$[[id]]
assert:
equals:
_ctx.response.body_json.success: true
register:
# 把响应体的 collection 字段赋值给 $[[collection]]
- collection: _ctx.response.body_json.collection
- request:
method: POST
# 在上个请求创建的集合里添加一个文档
url: http://127.0.0.1:9100/$[[collection]]/_doc
body: '{"hello": "world"}'
assert:
equals:
_ctx.response.body_json.result: created
上述示例中,我们利用动态注册的变量记录了每次测试创建的集合以便于后续请求使用。
最后的优化
为了使我们的配置更加灵活和“便携”,我们可以用环境变量来替换一些硬编码的值:
# loadgen.yml
variables:
- name: id
type: sequence
runner:
assert_error: true
assert_invalid: true
requests:
- request:
method: PUT
# 读取 PIZZA_SERVER 这个环境变量
url: $[[env.PIZZA_SERVER]]/test_create_document_$[[id]]
assert:
equals:
_ctx.response.body_json.success: true
register:
- collection: _ctx.response.body_json.collection
- request:
method: POST
url: $[[env.PIZZA_SERVER]]/$[[collection]]/_doc
body: '{"hello": "world"}'
assert:
equals:
_ctx.response.body_json.result: created
这样就可以通过:
PIZZA_SERVER=http://127.0.0.1:9101 loadgen -config loadgen.yml
在不同的 Pizza 服务上运行测试。
https://www.infinilabs.com/docs/latest/gateway/getting-started/benchmark ︎
https://www.infinilabs.com/docs/latest/gateway/getting-started/benchmark#变量的使用 ︎
https://www.infinilabs.com/docs/latest/gateway/getting-started/benchmark#返回值判断 ︎
https://www.infinilabs.com/docs/latest/gateway/getting-started/benchmark#动态变量注册 ︎
如何使用 Loadgen 来简化 HTTP API 请求的集成测试的更多相关文章
- 在Android中,使用Kotlin的 API请求简易方法
原文标题:API request in Android the easy way using Kotlin 原文链接:http://antonioleiva.com/api-request-kotli ...
- vue项目实践-添加axios封装api请求
安装 axios npm install axios --save 创建实例 (utils/fetch.js) axios 默认提交格式为:application/json 可使用 qs 模块(需要安 ...
- laravel5.7 前后端分离开发 实现基于API请求的token认证
最近在学习前后端分离开发,发现 在laravel中实现前后台分离是无法无法使用 CSRF Token 认证的.因为 web 请求的用户认证是通过Session和客户端Cookie的实现的,而前后端分离 ...
- 微信小程序开发——使用mock数据模拟api请求
前言: 微信小程序开发中,后端提供了接口设计文档,前端可以先mock数据模拟api请求进行开发调试,而且可以根据需要设计mock文件的格式和内容,这样在后端接口开发完成之前,前端可以最大限度的完成前端 ...
- [Postman]排除API请求(9)
可能存在API无法运行或出现意外行为的情况.如果您没有收到任何回复,邮递员将显示有关连接服务器时出错的消息. 有关错误可能原因的更多详细信息,请打开Postman Console.它有关于故障的详细信 ...
- Android开发 - Retrofit 2 使用自签名的HTTPS证书进行API请求
为了确保数据传输的安全,现在越来越多的应用使用Https的方式来进行数据传输,使用https有很多有点,比如: HTTPS协议是由SSL+HTTP协议构建的可进行加密传输.身份认证的网络协议,要比ht ...
- 解决React Native使用Fetch API请求网络报Network request failed
问题来源: 1 . 在测试fetch数据请求时,Xcode9.0以上的无法请求https, 需要在Xcode中加载项目后修改Info.plist的相关配置,具体如下参考 问题及解决方法一模一样,不再重 ...
- android Observable api请求参数设置注解问题
android Observable api请求参数设置注解问题 2018-10-29 20:05:24.919 11786-11786/xxx E/wxh: getQuote=USD getBase ...
- openstack身份认证与API请求流程
一.概况 1. 请求认证token时,需发送的认证信息包括: 2. 如果认证成功,会获得认证token 3. 在发送的API请求中将认证token填入X-Auth-Token字段.可以一直使用这个认证 ...
- Spring中统一相同版本的api请求路径的一些思考
Spring中统一相同版本的api请求路径的一些思考 问题场景 当我们在实际开发中,可能会遇到开发相同同版本的api, 假设相同版本的api请求路径为/v1/functionA,/v1/functio ...
随机推荐
- FasterViT:英伟达提出分层注意力,构造高吞吐CNN-ViT混合网络 | ICLR 2024
论文设计了新的CNN-ViT混合神经网络FasterViT,重点关注计算机视觉应用的图像吞吐能力.FasterViT结合CNN的局部特征学习的特性和ViT的全局建模特性,引入分层注意力(HAT)方法在 ...
- 力扣23(java)-合并k个升序链表(困难)
题目: 给你一个链表数组,每个链表都已经按升序排列. 请你将所有链表合并到一个升序链表中,返回合并后的链表. 示例 1: 输入:lists = [[1,4,5],[1,3,4],[2,6]]输出:[1 ...
- 力扣405(java)-数字转换为十六进制(简单)
题目: 给定一个整数,编写一个算法将这个数转换为十六进制数.对于负整数,我们通常使用 补码运算 方法. 注意: 十六进制中所有字母(a-f)都必须是小写.十六进制字符串中不能包含多余的前导零.如果要转 ...
- 聊聊 Linux iowait
哈喽大家好,我是咸鱼. 我们在使用 top 命令来查看 Linux 系统整体 CPU 使用情况的时候,往往看的是下面这一列: %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 ...
- 阿里巴巴大规模应用Flink的踩坑经验:如何大幅降低 HDFS 压力?
众所周知 Flink 是当前广泛使用的计算引擎,Flink 使用 checkpoint 机制进行容错处理[1],Flink 的 checkpoint 会将状态快照备份到分布式存储系统,供后续恢复使用. ...
- 深入浅出eBPF|你要了解的7个核心问题
简介: 过去一年,ARMS基于eBPF技术打造了Kubernetes监控,提供多语言无侵入的应用性能,系统性能,网络性能观测能力,验证了eBPF技术的有效性.eBPF技术和生态发展很好,未来前景广大, ...
- 为了让你在“口袋奇兵”聊遍全球,Serverless 做了什么?
简介: 江娱互动是一家新兴的游戏企业,自 2018 年成立伊始,江娱互动就面向广阔的全球游戏市场,通过创造有趣的游戏体验,在竞争激烈的游戏市场占得一席之地.仅仅 2 年的时间,江娱互动就凭借 Topw ...
- 技术干货 | jsAPI 方式下的导航栏的动态化修改
简介: 操作指导:通过 jsAPI 实现导航栏的动态修改. 很多开发同学在接入 H5 容器后都会对容器的导航栏进行深度定制,除了 Native 的定制化之外,还有很多场景是使用到 jsAPI 的 ...
- [FAQ] Error: Component series.bar not exists. Load it first. (echarts)
以上错误出现在使用 echarts 组件时,未导入或者使用不正确的情况下. 检查是否导入 line 或者 bar 这一类具体的 chart,比如: import 'echarts/lib/chart/ ...
- Oracle修改字段长度及属性
首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247486117&idx=1 ...