如何使用 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 ...
随机推荐
- EasyNLP带你实现中英文机器阅读理解
简介: 本⽂将提供对MacBERT模型的技术解读,以及如何在EasyNLP框架中使⽤MacBERT及其他预训练语言模型,进行中英文机器阅读理解任务的训练与预测. 作者:施晨.黄俊 导读 机器阅读理解是 ...
- MySQL统计信息不准导致的性能问题
简介: 统计信息不准导致错误的执行计划,引发性能问题 表的统计信息错误导致优化器选择错误的执行计划. 一个客户的性能优化案例: 没有修改数据库实例的任何配置参数以及业务代码没有变更的情况下,一条 sq ...
- 基于SLS构建RDS审计合规监控
简介: 数据库是企业业务的数据核心,其安全方面的问题在传统环境中已经成为泄漏和被篡改的重要根源.因此,对数据库的操作行为尤其是全量 SQL 执行记录的审计日志,就显得尤为重要. 背景 数据库是企业业务 ...
- [K8s] Pod 与容器设计模式 Sidecar
为什么 Pod 必须是原子调度? 在两个容器紧密协作的场景中,避免调度失败问题. Pod 解决了里面多个容器之间高效共享某些资源和数据,共享网络.共享存储. 容器设计模式? 通过在 Pod 里定义专门 ...
- SQL server 游标使用实例
--创建一个游标 DECLARE my_cursor CURSOR FOR SELECT id, Bran_number, Bran_taxis FROM dbo.Base_Branch; --打开游 ...
- FPGA最大工作频率教程
FPGA最大工作频率教程 1. Quartus的时序分析 作为编译过程的一部分,Quartus对布局布线的电路做时序分析.在编译报告里,展开"Timing Analyzer",这 ...
- 阿里巴巴MySQL开源中间件Canal入门
前言 距离上一篇文章发布又过去了两周,这次先填掉上一篇秒杀系统文章结尾处开的坑,介绍一下数据库中间件Canal的使用. Canal用途很广,并且上手非常简单,小伙伴们在平时完成公司的需求时,很有可能会 ...
- ChatGPT开源项目精选合集
大家好,我是 Java陈序员. 2023年,ChatGPT 的爆火无疑是最值得关注的事件之一,AI对话.AI绘图等工具层出不穷. 今天给大家介绍几个 ChatGPT 的开源项目! 关注微信公众号:[J ...
- JUC并发编程学习笔记(二)Lock锁(重点)
Lock锁(重点) 传统的synchronized 传统的解决多线程并发导致的一些问题我们会使用synchronized关键字来解决,synchronized的本质就是队列.锁. Lock的实现类有: ...
- docker-compose 安装redis
一. docker 拉去最新版本的redis `docker pull redis:6.0.6` #后面可以带上tag号, 默认拉取最新版本 二. docker安装redis 执行命令: mkdir ...