Package: httptesting
HttpTestingHttpTesting 是HTTP(S)协议接口测试框架，通过YAML来编写测试用例，通过命令行运行代码，不固定目录结构，支持通过命令行生成脚手架。功能描述httptesting通过YAML编写测试用例，安装httptesting后通过amt命令执行测试用例，支持指定YAML中CASE名称进行单用例执行，支持指定请求头默认值来共享请求头，支持自定义扩展功能(在case执行根目录下创建extfunc.py文件来自定义代码)。
支持多进程执行用例，支持用例执行出错重试功能，支持设定执行用例次数；支持设置控制台输出和报告输出；支持参数化功能与用户自定义用户变量。安装包下载:https://pypi.org/project/httptesting/#files源码：https://github.com/HttpTesting/pyhttp版本信息序号版本号描述1v1.0使用unittest框架2v1.1使用pytest框架快速开始安装pip 包管理直接安装- pip install HttpTesting使用命令运行通过am命令加上不同的参数来驱动执行；注意区分参数大小写；am序号命令参数描述1am -conf set 或--config set此命令用来设置基本配置2am -f template.yaml或--file template.yaml执行YAML用例，支持绝对或相对路径3am -d testcase或--dir testcase批量执行testcase目录下的YAML用例，支持绝对路径或相对路径4am -sp demo或--startproject demo生成脚手架demo目录,以及用例模版5am -har httphar.har根据抓包工具导出的http har文件，生成测试用例YAML6am -c demo.yaml或--convert demo.yaml转换数据为HttpTesting测试用例基本配置[通过开关启用功能：并发执行, 失败重新执行, 用例执行次数, Debug模式，输出模式(html与控制台)，URL基本路径]URL设置钉钉机器人设置测试报告设置EMAIL邮箱设置用例执行配置用例执行YAML执行:[整个YAML文件执行，指定CASE名称执行，批定多个CASE名称执行并且按指定顺序执行]am -f template.yamlam -f template.yaml Case1am -f template.yaml Case2 Case1YAML批量执行:[批量执行testcase目录下所有YAML测试用例文件]am -d testcase脚手架生成am -sp demo 此命令生成一个demo文件夹结构。脚手架功能,是生成一个测试用例结构与Case模版.HAR转换，通过charles抓包工具，导出的har文件为工具能识别的yaml用例文件；执行命令: am -har httphar.har  自动生成httptesting用例 har_testcase.yaml。har命令来解析, Charles抓包工具导出的http .har请求文件, 自动生成HttpTesting用例格式.用例编写YAML用例格式场景模式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', '修改成功']多CASE模式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中的变量及函数引用方式httptesting采用双${变量}$为变量标识符；%{函数名称}%为函数标识；自定义变量变量作用域为当前CASE.通过在Case下定义USER_VAR字段，来自定义变量USER_VAR字段下定义的字段为用户变量，作用于当前Case示例1(自定义变量使用)TESTCASE: 
	Case1:
		- 
			Desc: 接口详细描述
			USER_VAR:
				token:  xxxxxxxx
		-
			Url: /xxxx/xxxx
			Method: POST
			Headers: 
				token: ${token}$
			Data:
			OutPara:
			Assert: []示例2(自定义变量)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字段变量使用OutPara字段用来做公共变量,供其它接口使用,默认为"";示例: "H_token": result.data 是请求结果，返回的嵌套级别,使用方法: ${H_token}$OutPara为dict类型,可以做多个公共变量.Assert断言Assert字段默认为[].序号断言方法断言描述1eq: [a, b]判断 a与b相等,否则fail2nq: [a, b]判断 a与b不相等,否则fail3al: [a, b]判断 a is b 相当于id(a) == id(b),否则fail4at: [a, b]判断 a is not b 相当于id(a) != id(b)5inc: [a, b]判断 a in b ,否则fail6ninc: [a, b]判断 a in not b,否则fail7ais: [a, b]判断 isinstance(a, b) True8anis: [a, b]判断 isinstance(a, b) False9ln: [a]判断 a is None,否则fail10lnn: [a]判断 a is not none11bt: [a]判断 a 为True12bf: [a]判断 a 为False内置函数及扩展需要注意的是，暂时不支持函数嵌套使用，即在在函数中直接引用另一个函数，如果需要引用可以将另一个函数放在USER_VAR中。使用原型(带参数与不带参数)"%{md5('aaaa')}%" 或 "%{timestamp()}%"函数名参数说明md5txt字符串生成md5字符串示例: cbfbf4ea6d7c8032584dcf0defa10276timestamp-秒级时间戳示例: 1563183829uuid1-生成唯一id,uuid1示例:ebcd6df8a77611e99bb588b111064583datetimestr-生成日期时间串,示例:2019-07-16 10:50:16mstimestamp-毫秒级时间戳,20位sleep_time-线程睡眠,0.5为500毫秒，1为1秒rnd_list[]随机从列表中选择值datetime_fmt格式化字符串"%{datetime_fmt('Y-m-d H')}%:59:59"sum两数之和"%{sum(var1,var2)}%"其它后续添加自定义函数扩展功能说明在执行用例root目录，新建extfunc.py文件按模型自定义函数类名 Extend不可更改@staticmethod函数必须定义为静态函数各数不做限制自定义函数扩展功能模型class Extend:
	@staticmethod
	def func1():
		return 'ext func'
	
	@staticmethod
	def func2(args):
		return args使用示例1："%{func1()}%"使用示例2: "%{func1('aaaa')}%"参数化功能定义参数化参数后，同一用例会按照参数个数决定用例执行次数。通过在用例Case下定义PARAM_VAR字段PARAM_VAR字段下定义参数化变量，供之后引用如果在PARAM_VAR下定义多个，参数化变量，参数个数要匹配。现在如果定义了多个参数化变量，执行用例的次数是排列组合数。之后如有必要会改成，按参数化变量内参数的各数决定执行用例次数。参数化功能模型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需要注意事项：PARAM_VAR: 下可以有多个参数化变量，必须是key value键值对，并且value必须是list类型PARAM_VAR：下有多个参数时，value各数必须相等。以上例子中为倒，生成的参数是${sig}$ , ${rem}$ 对应的值为["1", "4"] ,["2", "5"],["3", "6"] ,["4", "7"]value各数决定了参数化的case行数。上例会生成4个参数组合，也就是生成4条case。常用对象(通常做参数变量时使用)res: 请求Response对象result: res.json 或 res.textcookie: res.cookie 响应cookie字典对象;  当做为参数时如果cookie.SESSION这样的写法代表取cookie中的SESSION对象. 如果只写cookie,会解析成"SESSION=xxxxxxx; NAME=xxxxxx"headers: res.headers 响应头字典对象header: header.content-type 请求头对象用例执行1、生成脚手架2、编写脚手架中testcase下YAML模版用例3、切换到testcase目录4、amt -dir testcase 自动运行testcase下YAML用例5、自动生成测试报告Html框架基本配置1、通过命令打开框架config.yaml2、amt -conf set  ; amt -conf getamt -conf set => allam -conf set BASE_URL=http://api.xxx.netam -conf get BASE_URL  =>http://api.xxx.net3、修改基本配置，并保存新增功能指定case编号执行指定单个Case执行 amt -f xxxx.yaml Case1指定多个Case执行 amt -f xxxx.yaml Case1 Case2 Case3智能URL当用例中指定全路径时，自去取该路径，当不是绝对路径时，取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在Case中增加REQ_HEADER字段来做为公共的请求头。之后Case中执行共享此请求头如果在用例中设置了REQ_HEADER字段与请求中也单独设置了请求头，那么第一顺序为请求中的为主。上边的例子为场景用例，由两个请求组成，请求1，使用的是请求头默认值，请求2，使用自身请求头。Case执行顺序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]以上示例Order字段为执行Case权重，全局根所此字段来排执行顺序。默认为0，字段可省略。Order越大，越靠后执行。CSV文件参数化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]在用例结果头部增加CSV_VAR字典对象，并指定file_path key值，为xxxx.csv文件路径如果用例头部存在CSV_VAR说明启用CSV参数化。参数化使用时CSV列名，即为引用字段名，引用方法"${字段名}$.参数化需注意，CSV每一行为一个CASE。PARAM_VAR也为参数化功能，当与CSV_VAR同时存在时，以CSV为准Case跳过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, ""]在用例头部分，增加Skip: True标记该case跳过Skip: False或Skip字段不存在则不会跳过用例执行场景中单接口跳过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]从以上用例中可以看出，第二个接口中多了一个Skip: True字段，这样执行时，就会跳过该接口，只执行第一个接口。skip支持自定义函数：Skip: "%{skip_func()}%"; 但是返回参数必须是True 或 'True'才能跳过。功能对比序号功能V1.0V1.1配置参数1并发执行-√ENABLE_EXECUTION:False EXECUTION_NUM: 42失败重新执行√√ENABLE_RERUN: False  RERUN_NUM:  23重复执行-√ENABLE_REPEAT: False REPEAT_NUM: 24钉钉消息√√ENABLE_DDING:  False5发送报告邮件√√EMAIL_ENABLE: False6控制台输出-√ENABLE_EXEC_MODE: False7自定义函数扩展√√用例执行root目录增加extfunc.py8自定义变量√√在用例中用USER_VAR字段定义变量，作用于当前Case9用例参数化√√在用例中用PARAM_VAR字段定义参数化变量,作用于当前Case10请求头默认值√√设置用例请求头默认值,整个case共享请求头。11指定case执行-√单个yaml文件指定case执行12Case执行顺序-√通过Order字段设置Case执行优先级13Csv参数化-√通过外部csv文件进行参数化14case跳过-√通过在case中增加标记Skip: True15场景中单接口跳过-√通过在case中增加标记Skip: True代码打包与上传PyPi通过setuptools工具进行框架打包,需要编写setup.py打包：python3 setup.py bdist_wheel上传PyP: twine upload dist/*通过poetry工具打包poetry buildpoetry config repositories.testpypihttps://pypi.org/project/pyhttppoetry pushlish  输入pypi用户名和密码