![Maven Central Adds Sigstore Signature Validation](https://cdn.sanity.io/images/cgdhsj6q/production/7da3bc8a946cfb5df15d7fcf49767faedc72b483-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Maven Central Adds Sigstore Signature Validation
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.
HttpTesting 是HTTP(S)协议接口测试框架,通过YAML来编写测试用例,通过命令行运行代码,不固定目录结构,支持通过命令行生成脚手架。
httptesting通过YAML编写测试用例,安装httptesting后通过amt命令执行测试用例,支持指定YAML中CASE名称进行单用例执行,支持指定请求头默认值来共享请求头,支持自定义扩展功能(在case执行根目录下创建extfunc.py文件来自定义代码)。 支持多进程执行用例,支持用例执行出错重试功能,支持设定执行用例次数;支持设置控制台输出和报告输出;支持参数化功能与用户自定义用户变量。
https://pypi.org/project/httptesting/#files
https://github.com/HttpTesting/pyhttp
序号 | 版本号 | 描述 |
---|---|---|
1 | v1.0 | 使用unittest框架 |
2 | v1.1 | 使用pytest框架 |
- pip install HttpTesting
通过am命令加上不同的参数来驱动执行;注意区分参数大小写;
序号 | 命令参数 | 描述 |
---|---|---|
1 | am -conf set 或--config set | 此命令用来设置基本配置 |
2 | am -f template.yaml或--file template.yaml | 执行YAML用例,支持绝对或相对路径 |
3 | am -d testcase或--dir testcase | 批量执行testcase目录下的YAML用例,支持绝对路径或相对路径 |
4 | am -sp demo或--startproject demo | 生成脚手架demo目录,以及用例模版 |
5 | am -har httphar.har | 根据抓包工具导出的http har文件,生成测试用例YAML |
6 | am -c demo.yaml或--convert demo.yaml | 转换数据为HttpTesting测试用例 |
[通过开关启用功能:并发执行, 失败重新执行, 用例执行次数, Debug模式,输出模式(html与控制台),URL基本路径]
URL设置
钉钉机器人设置
测试报告设置
EMAIL邮箱设置
用例执行配置
YAML执行:
[整个YAML文件执行,指定CASE名称执行,批定多个CASE名称执行并且按指定顺序执行]
am -f template.yaml
am -f template.yaml Case1
am -f template.yaml Case2 Case1
YAML批量执行:
[批量执行testcase目录下所有YAML测试用例文件]
am -d testcase
am -sp demo 此命令生成一个demo文件夹结构。
脚手架功能,是生成一个测试用例结构与Case模版.
转换,通过charles抓包工具,导出的har文件为工具能识别的yaml用例文件;
执行命令: am -har httphar.har 自动生成httptesting用例 har_testcase.yaml。
har命令来解析, Charles抓包工具导出的http .har请求文件, 自动生成HttpTesting用例格式.
TESTCASE:
#Case1由登录,编辑两个接口组成的场景用例
Case1:
-
Desc: xxxx业务场景(登录->编辑)
-
Desc: 登录接口
Url: /login/login
Method: GET
Headers:
content-type: "application/json"
cache-control: "no-cache"
Data:
name: "test"
pass: "test123"
OutPara:
"H_token": result.data
"content_type": header.content-type
"name": Data.name
"pass": Data.pass
Assert:
- eq: [result.status, 'success']
-
Desc: 编辑接口
Url: /user/edit
Method: GET
Headers:
content-type: "${content_type}$"
cache-control: "no-cache"
token: "$H_token$"
Data:
name: "${name}$"
pass: "${pass}$"
OutPara:
"$H_token$": result.data
Assert:
- ai: ['success', result.status]
- eq: ['result.status', '修改成功']
1.多CASE由两种组成,第1种,在YAML中写多个Case1...CaseN
2.第2种,通过参数化的型势,关于参数化,文档参数化部分有介绍
TESTCASE:
#同一接口,不同参数,扩充为多个CASE,Case1为场景用例,Case2为单接口用例
Case1:
-
Desc: 登录接口-正常登录功能
-
Desc: 登录接口
Url: /login/login
Method: GET
Headers:
content-type: "application/json"
cache-control: "no-cache"
Data:
name: "test"
pass: "test123"
OutPara:
"H_cookie": cookie.SESSION
Assert:
- eq: [result.status, 'success']
Case2:
-
Desc: 登录接口-错误密码
-
Desc: 登录接口
Url: /login/login
Method: GET
Headers:
content-type: "application/json"
cache-control: "no-cache"
Data:
name: "test"
pass: "test123"
OutPara:
"H_cookie": cookie.SESSION
Assert:
- eq: [result.status, 'error']
httptesting采用双${变量}$为变量标识符;%{函数名称}%为函数标识;
变量作用域为当前CASE.
TESTCASE:
Case1:
-
Desc: 接口详细描述
USER_VAR:
token: xxxxxxxx
-
Url: /xxxx/xxxx
Method: POST
Headers:
token: ${token}$
Data:
OutPara:
Assert: []
TEST_CASE:
Case1:
-
Desc: 扫码校验券(支持检测微信券二维码码、微信会员h5券二维码、条码)
USER_VAR:
version: 1.0
data:
req:
sid: '1380598237'
wxcode: "164073966187485312752286" #209736174428
appid: dp0Rm4wNl6A7q6w1QzcZQstr
sig: 9c8c96b38d759abe6633c124a5d37225
v: "${version}$"
ts: 1564643536
- Desc: 扫码校验券
Url: /pos/checkcoupon
Method: POST
Headers:
content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
cache-control: no-cache
Data: ${data}$
OutPara:
Assert:
- eq: [result.errcode, 0]
以上通过USER_VAR字典对象来定义变量, key为变量名, value为变量值; 使用方法: ${token}$
无需定义变量, USER_VAR字段在用例中,可以省略.
OutPara字段用来做公共变量,供其它接口使用,默认为"";
Assert字段默认为[].
序号 | 断言方法 | 断言描述 |
---|---|---|
1 | eq: [a, b] | 判断 a与b相等,否则fail |
2 | nq: [a, b] | 判断 a与b不相等,否则fail |
3 | al: [a, b] | 判断 a is b 相当于id(a) == id(b),否则fail |
4 | at: [a, b] | 判断 a is not b 相当于id(a) != id(b) |
5 | inc: [a, b] | 判断 a in b ,否则fail |
6 | ninc: [a, b] | 判断 a in not b,否则fail |
7 | ais: [a, b] | 判断 isinstance(a, b) True |
8 | anis: [a, b] | 判断 isinstance(a, b) False |
9 | ln: [a] | 判断 a is None,否则fail |
10 | lnn: [a] | 判断 a is not none |
11 | bt: [a] | 判断 a 为True |
12 | bf: [a] | 判断 a 为False |
需要注意的是,暂时不支持函数嵌套使用,即在在函数中直接引用另一个函数,如果需要引用可以将另一个函数放在USER_VAR中。
使用原型(带参数与不带参数)
函数名 | 参数 | 说明 |
---|---|---|
md5 | txt字符串 | 生成md5字符串示例: cbfbf4ea6d7c8032584dcf0defa10276 |
timestamp | - | 秒级时间戳示例: 1563183829 |
uuid1 | - | 生成唯一id,uuid1示例:ebcd6df8a77611e99bb588b111064583 |
datetimestr | - | 生成日期时间串,示例:2019-07-16 10:50:16 |
mstimestamp | - | 毫秒级时间戳,20位 |
sleep_time | - | 线程睡眠,0.5为500毫秒,1为1秒 |
rnd_list | [] | 随机从列表中选择值 |
datetime_fmt | 格式化字符串 | "%{datetime_fmt('Y-m-d H')}%:59:59" |
sum | 两数之和 | "%{sum(var1,var2)}%" |
class Extend:
@staticmethod
def func1():
return 'ext func'
@staticmethod
def func2(args):
return args
定义参数化参数后,同一用例会按照参数个数决定用例执行次数。
TEST_CASE:
Case2:
- Desc: 给指定用户发送验证码
USER_VAR:
cno_list:
- '1674921314241197'
- '1581199496593872'
- '1623770534820512'
- '1674921701066628'
- '1581199096195979'
- '1623770606653991'
PARAM_VAR:
sig: ["1", "2", "3", "4"]
rem: ["4", "5", "6", "7"]
- Desc: 给指定用户发送验证码
Url: /user/sendcode
Method: POST
Headers:
content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
cache-control: no-cache
Data:
req:
cno: '%{rnd_list("${cno_list}$")}%'
appid: dp1svA1gkNt8cQMkoIv7HmD1
sig: "${sig}$"
v: 2.0
ts: 123
OutPara: null
Assert:
- eq:
- result.errcode
- 0
- eq:
- result.res.result
- SUCCESS
当用例中指定全路径时,自去取该路径,当不是绝对路径时,取BASE_URL进行智能拼接。
TEST_CASE:
Case1:
- Desc: 给用户发送验证码业务场景(发送1->发送2)
USER_VAR:
cno_list:
- '1674921314241197'
- '1581199496593872'
- '1623770534820512'
- '1674921701066628'
- '1581199096195979'
- '1623770606653991'
REQ_HEADER:
content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
cache-control: no-cache
- Desc: 给指定用户发送验证码1
Url: /user/sendcode
Method: POST
Data:
req:
cno: '%{rnd_list("${cno_list}$")}%'
appid: dp1svA1gkNt8cQMkoIv7HmD1
sig: "123"
v: 2.0
ts: 123
OutPara: null
Assert:
- eq:
- result.errcode
- 0
- eq:
- result.res.result
- SUCCESS
- Desc: 给指定用户发送验证码2
Url: /user/sendcode
Method: POST
Data:
req:
cno: '%{rnd_list("${cno_list}$")}%'
appid: dp1svA1gkNt8cQMkoIv7HmD1
sig: "123"
v: 2.0
ts: 123
Headers:
content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
cache-control: no-cache
OutPara: null
Assert:
- eq:
- result.errcode
- 0
- eq:
- result.res.result
- SUCCESS
TEST_CASE:
Case1:
-
Desc: 给用户发送验证码业务场景(发送1->发送2)
Order: 10
USER_VAR:
cno_list:
- '1674921314241197'
- '1581199496593872'
- '1623770534820512'
- '1674921701066628'
- '1581199096195979'
- '1623770606653991'
REQ_HEADER:
content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
cache-control: no-cache
- Desc: 给指定用户发送验证码1
Url: /user/sendcode
Method: POST
Data:
req:
cno: '%{rnd_list("${cno_list}$")}%'
appid: dp1svA1gkNt8cQMkoIv7HmD1
sig: "123"
v: 2.0
ts: 123
OutPara: null
Assert:
- eq: [result.errcode, 0]
TEST_CASE:
Case1: #用例1
-
Desc: 当日储值统计/charge/today
USER_VAR:
appkey: '0100ff174e808de80db21152ca7dde31'
PARAM_VAR:
sig: ["1", "2"]
rem: ["4", "5"]
CSV_VAR:
file_path: 'd:/deal.csv'
Order: 20
-
Desc: 当日储值统计
Url: /charge/today
Method: POST
Headers:
content-type: "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
cache-control: "no-cache"
Data:
req:
begin_time: "${name}$"
end_time: "${age}$"
shop_id: 1512995661
appid: 'aaaaa'
sig: "%{sign({'data': 'data.req', 'appid':'data.appid', 'ts':'data.ts', 'v': 'data.v','appkey':'${appkey}$'})}%"
v: 2.0
ts: 1564967996
OutPara:
Assert:
- eq: [result.errcode, 1006]
TEST_CASE:
Case1: #用例1
-
Desc: 会员标记编辑->获取
USER_VAR:
appkey: '4b6ef4ee839dfb0922c28e97143d371e'
Skip: True
-
Desc: 第三方收银会员标记-编辑接口
Url: /userremark/edit
Method: POST
Headers:
content-type: "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
cache-control: "no-cache"
Data:
req:
uid: "384345911306964992"
token: "adc97617d8c42cd1c99211cab81a2a80"
remark: "123456"
appid: "dp3wY4YtycajNEz23zZpb5Jl"
sig: "%{sign({'data': 'data.req', 'appid':'data.appid', 'ts':'data.ts', 'v': 'data.v','appkey':'${appkey}$'})}%"
v: 2.0
ts: 1564967996
OutPara:
token: data.req.token
remark: data.req.remark
uid: data.req.uid
Assert:
- eq: [result.errcode, 0]
- eq: [result.res, ""]
TEST_CASE:
Case1:
-
Desc: 当日储值统计/charge/today
USER_VAR:
appkey: '0100ff174e808de80db21152ca7dde31'
# CSV_VAR:
# file_path: 'd:/deal.csv'
Order: 20
-
Desc: 当日储值统计1
Url: /charge/today
Method: POST
Headers:
content-type: "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
cache-control: "no-cache"
Data:
req:
begin_time: "aaaa"
end_time: "bbbb"
shop_id: 1512995661
appid: 'aaaaa'
sig: "%{sign({'data': 'data.req', 'appid':'data.appid', 'ts':'data.ts', 'v': 'data.v','appkey':'${appkey}$'})}%"
v: 2.0
ts: 1564967996
OutPara:
Assert:
- eq: [result.errcode, 1006]
-
Desc: 当日储值统计2
Skip: True
Url: /charge/today2
Method: POST
Headers:
content-type: "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
cache-control: "no-cache"
Data:
req:
begin_time: "aaaa"
end_time: "dddd"
shop_id: 1512995661
appid: 'aaaaa'
sig: "%{sign({'data': 'data.req', 'appid':'data.appid', 'ts':'data.ts', 'v': 'data.v','appkey':'${appkey}$'})}%"
v: 2.0
ts: 1564967996
OutPara:
Assert:
- eq: [result.errcode, 1006]
序号 | 功能 | V1.0 | V1.1 | 配置参数 |
---|---|---|---|---|
1 | 并发执行 | - | √ | ENABLE_EXECUTION:False EXECUTION_NUM: 4 |
2 | 失败重新执行 | √ | √ | ENABLE_RERUN: False RERUN_NUM: 2 |
3 | 重复执行 | - | √ | ENABLE_REPEAT: False REPEAT_NUM: 2 |
4 | 钉钉消息 | √ | √ | ENABLE_DDING: False |
5 | 发送报告邮件 | √ | √ | EMAIL_ENABLE: False |
6 | 控制台输出 | - | √ | ENABLE_EXEC_MODE: False |
7 | 自定义函数扩展 | √ | √ | 用例执行root目录增加extfunc.py |
8 | 自定义变量 | √ | √ | 在用例中用USER_VAR字段定义变量,作用于当前Case |
9 | 用例参数化 | √ | √ | 在用例中用PARAM_VAR字段定义参数化变量,作用于当前Case |
10 | 请求头默认值 | √ | √ | 设置用例请求头默认值,整个case共享请求头。 |
11 | 指定case执行 | - | √ | 单个yaml文件指定case执行 |
12 | Case执行顺序 | - | √ | 通过Order字段设置Case执行优先级 |
13 | Csv参数化 | - | √ | 通过外部csv文件进行参数化 |
14 | case跳过 | - | √ | 通过在case中增加标记Skip: True |
15 | 场景中单接口跳过 | - | √ | 通过在case中增加标记Skip: True |
打包:python3 setup.py bdist_wheel
上传PyP: twine upload dist/*
poetry build
poetry config repositories.testpypi https://pypi.org/project/pyhttp
poetry pushlish 输入pypi用户名和密码
FAQs
Httptesting HTTP(s) interface testing framework.
We found that httptesting demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.