一、HTTPRUNNER2.x
1.1、工具介绍
- HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求。
1.2、设计思想
- 充分复用优秀的框架组装成更强大完整的框架
- 必须遵循 约定大于配置 的准则,在自动化测试中充分体现
- 追求投入产出比,一份投入即可实现多种测试需求
1.3、核心功能
- 继承Requests的全部功能
- 采用json/yaml格式作为测试用例数据载体
- 支持debugtalk.py辅助函数,增强脚本实践中的灵活性,实现动态计算逻辑
- 支持测试用例分层,实现测试用例复用:数据层、业务(逻辑层)层
- 支持unittest框架的setup、teardown处理机制:hook机制
- 支持har录制脚本生成测试用例(har2case)
- 结合locust框架,支持分布式性能测试
- 支持CLI命令行调用模式,便于jenkins集成
- 展示层:测试结果统计及优美的HTML报告+日志
- 扩展性:支持二次开发及实现web平台化
1.4、关于分层测试思想
区分一下自动化测试分层和测试框架分层、项目结构分层
- 所谓自动化测试分层,根据投入与产出比自上而下:UI(系统测试)、服务(api/集成测试)、底层(单元测试)
- 测试框架分层,能实现自动化测试的有:接口、UI、性能等;均采用测试数据与脚本分离策略
-
- 接口自动化测试框架分层:数据层(测试数据/用例)、业务层(逻辑层)、公共层(业务调用的公共方法)
-
- UI自动化测试框架分层(以PO设计模式为例):页面对象层(包含元素定位)、业务逻辑层(调用页面对象)、测试数据层(数据与脚本分离)
- 实现测试分层后搭建工程结构:业务层(调用或组装测试用例)、数据层(数据与测试用例/脚本分离)、公共库(基础类公共方法)、工具层(日志系统、数据库处理、报告处理)、展示层(HTML报告展示)、持久层(譬如用例、html报告等是否存放数据库)
二、搭建开发环境
我们是测试,为啥叫开发环境?因为测试的所有脚本及数据调试都是在本地开发测试完成,故而叫开发环境。测试环境适用于投产前的测试验证。
2.1、python环境
虽然官方支持了python2和python3,但是在咱们实际开发中,建议还是使用python3且在3.6及以上版本。
2.2、httprunner安装方式
为啥要指定安装版本?因为当前httprunner托管在PyPI上的稳定版已经是3.x版本了。
- pip install httprunner==2.3.2
版本升级
- pip install -U HttpRunner
安装校验
- hrun -V 或者–help
- har2case -V
2.3、关于python开发环境
实际开发过程中,建议养成良好的开发模式,在不同工程中创建不同的python虚拟开发环境,一则便于管理,二则便于工程迁移
创建虚拟环境的几种方式,有很多:推荐anaconda或者python -m venv
- python -m venv /path虚拟路径
三、基础概念
建议:在想使用某个工具/框架之前,先理解它的原理/设计思想,再弄明白与之对应理论概念。
- 测试用例(testcase)、测试步骤(teststep)、测试用例集(testsuite)
-
- 测试用例集testsuite下有testcases包含多个testcase
{
"config": {
"name": "测试用例集:业务名称",
"variables": {
"token": "存放公共变量,可以在testcases下testcase中使用",
},
"base_url": "${ENV(URL)}"
},
"testcases": [{
"name": "可以是某个测试用例或api",
"testcase": "testcases/homeIndex.json"
},
{
"name": "支持组装多个测试用例",
"testcase": "testcases/myPractiecs.json"
}
]
}
-
- 测试用例testcase下有一个或多个test,它又能包含api、testcase;关于teststeps是在har2case转换录制的har文件才有的内容,如下示例:
{
"config": {
"name": "可以描述一个业务场景或者单个用例业务",
"base_url": "https://www.xxx.com"
},
"teststeps": [{
"name": "接口业务名称描述",
"skip": "skip login",
"request": {
"url": "/xx/xx/passwordLogin",
"method": "POST",
"headers": {
"Content-Type": "application/json;charset=UTF-8",
"User-Agent": "IeltsBroV3/10.0.0 (com.yasiBro.v2; build:5; iOS 13.3.0) Alamofire/4.9.0"
},
"json": {
"password": "111111",
"deviceid": "99B5D095-67AD-4B79-A995-6C869A895873",
"mobile": "13800138000",
"pushToken": "d90e1e770a37ac3464930c968fed9885",
"deviceName": "iPhone 8 Plus",
"verifyCode": "",
"loginType": 0,
"channel": 0,
"deviceType": "ios",
"zone": "86"
}
},
"extract": [{
"token": "content.content.token"
}],
"validate": [{
"eq": [
"status_code",
200
]
}
]
}]
}
-
- 框架中正常演示的是
[{
"config": {
"name": "全局设置.",
"variables": {
"user_agent": "存放灵活变量"
},
"verify": false
}
},
{
"test": {
"name": "调用api拿到响应token",
"api": "api/login.yml",
"extract": {
"token": "content.content.token"
},
"validate": [{
"eq": [
"status_code",
200
]
}]
}
},
{
"test": {
"name": "请求某个业务接口",
"api": "api/get_status.yml",
"validate": [{
"eq": [
"status_code",
200
]
}]
}
}
]
-
- api的写法
{
"name": "登录接口",
"base_url": "${ENV(URL)}",
"variables": {
"expected_status_code": 200
},
"request": {
"url": "/xxx/xxx/passwordLogin",
"method": "POST",
"json": {
"appVersion": 9,
"channel": 1,
"deviceName": "HUAWEI EML-AL00",
"deviceType": "android",
"deviceid": "0795cd72-3e05-40ba-9733-5df1c1fa0970",
"loginType": 0,
"mobile": "${ENV(MOBILE)}",
"password": "${ENV(PASSWORD)}",
"pushToken": "25b9b066dc939b9863fe9feb3fca654d",
"systemVersion": "8.1.0",
"zone": 86
}
},
"validate": [{
"eq": [
"status_code",
200
]
}]
}
这个框架还是建议使用yaml格式作为测试用例数据载体,看这个括号都眼花缭乱,维护起来也比较麻烦,yaml是一种dict or list的结果,可读性更好!
- api示例如下(是不是清晰可人):
name: 登录接口
base_url: ${ENV(URL)}
variables:
expected_status_code: 200
request:
url: /hcp/apiLogin/passwordLogin
method: POST
json:
appVersion: 9.0
channel: 1
deviceName: HUAWEI EML-AL00
deviceType: android
deviceid: 0795cd72-3e05-40ba-9733-5df1c1fa0970
loginType: 0
mobile: ${ENV(MOBILE)}
password: ${ENV(PASSWORD)}
pushToken: 25b9b066dc939b9863fe9feb3fca654d
systemVersion: 8.1.0
zone: 86
validate:
- eq: [status_code, 200]
3.1、快速应用
上面已经对httprunner框架的基础概念有了了解,使用它的特性,就可以快速开展测试工作。
- 简要说明如何快速使用这个框架吧(文字描述版)
第一步:抓包,charles导出har数据包
第二步:转换,har2case xxx.har 默认json格式,-2y参数表示yaml格式
第三步:执行,hrun xxx.yml ,执行完毕当前目录下生成reports/xxx.html报告
3.2、应该要掌握的知识点
3.2.1、hook机制
测试用例testcase,config新增关键字:setup_hooks、teardown_hooks,表示在测试用例执行前后用于准备或清理工作
- setup_hooks
- teardown_hooks
-
- 测试用例层testcase
- config:
name: 基础配置
request:
base_url: http://127.0.0.1:8080/
setup_hooks:
- ${hook_print(setup)}
teardown_hooks:
- ${hook_print(teardown)}
-
- 测试步骤层teststep
- test:
name: 获取请求状态
request:
url: /get_status
method: GET
teardown_hooks:
- ${get_status($response)}
validate:
- eq: ["status_code", 500]
3.2.2、环境变量
应用场景:除了信息安全方面的考虑,还应该考虑如何切换环境、切换配置,故而需要使用全局或局部变量
- 该框架中可以在任意目录下新增.env文件(作用于当前执行目录),设置环境变量
ACCOUNT=13800013800
PASSWD=111111
BASEURL=https://www.baidu.com
- 引用:{ENV(ACCOUNT)}\{ENV(PASSWD)}${ENV(BASEURL)}
3.2.3、数据驱动
这里不再演示,在其他文章或网络都有详细使用说明,只说它支持的方式。
- 在 YAML/JSON 中直接指定参数列表,借用httprunner使用文档中的例子演示
config:
name: "demo"
testcases:
testcase1_name:
testcase: /path/to/testcase1
parameters:
# 如下就是直接的列表形式,如果是字典多列表,参数使用-连接:user_agent-version,参数为嵌套列表的字典
user_agent: ["iOS/10.1", "iOS/10.2", "iOS/10.3"]
testcase2_name:
testcase: /path/to/testcase2
parameters:
# 如果是字典多列表,参数使用-连接:user_agent-version,参数为嵌套列表的字典
user_agent-version:
- ["iOS/10.1", "10.1"]
- ["iOS/10.2", "10.2"]
- 通过内置的 parameterize(可简写为P)函数引用 CSV 文件
config:
name: "demo"
testcases:
testcase1_name:
testcase: /path/to/testcase1
parameters:
# 数据量比较大的时候使用
user_id: ${P(data/user_id.csv)}
- 调用 debugtalk.py 中自定义的函数生成参数列表,此方式比较灵活
def get_account(num):
accounts = []
for index in range(1, num+1):
accounts.append(
{"username": "user%s" % index, "password": str(index) * 6},
)
return accounts
-
- debugtalk定义的函数在测试用例文件中引用如下${func_name(parameters)}:
config:
name: "demo"
testcases:
testcase1_name:
testcase: /path/to/testcase1
parameters:
username-password: ${get_account(10)}
3.2.4、关键字应用
这里引用的包括但不局限用例的组成部分
- extract提取关键字,提取当前接口响应参数作为下个接口的请求参数,参数关联时使用
- validate响应断言
- parameters全局参数
- setup_hooks、teardown_hooks框架支持的hook机制,它的函数放置在debugtalk中,
- variables测试步骤中定义的变量,作用域当前文件,但是支持从testsuite覆盖testcase
- base_url在testcase中path部分可以是完整地址,但是不能在有base_url的情况下
- api可以在testcase中调用api层的测试用例
- testcase同api有一样的效果
- output整个用例输出的参数列表
这里演示的用例文件都是yaml格式为准
api演示示例,包含上面的基本关键字,先写debugtalk函数
def set_up():
"""在config层做准备工作"""
print("我在config层做准备")
def tear_down():
"""在config层做准备工作"""
print("我在config层清理数据")
def output_request(request):
"""setup,打印请求参数"""
print(request)
def output_response(response):
"""teardown,打印响应结果"""
print(response.text)
再组织api文件,两份:有单个的api及testcase
-
- 单个api
name: 登录接口
base_url: 接口请求地址
variables:
expected_status_code: 200
mobile: 你的账号
passwd: 你的密码
request:
url: /xxx/xxx/passwordLogin
method: POST
json:
appVersion: 9.0
channel: 1
deviceName: HUAWEI EML-AL00
deviceType: android
deviceid: 0795cd72-3e05-40ba-9733-5df1c1fa0970
loginType: 0
mobile: $mobile
password: $passwd
pushToken: 25b9b066dc939b9863fe9feb3fca654d
systemVersion: 8.1.0
zone: 86
validate:
- eq: [status_code, $expected_status_code]
-
- 测试用例
- config:
name: "集合关键字使用"
variables:
user_agent: 'iOS/10.3'
verify: False
setup_hooks:
- ${set_up()}
teardown_hooks:
- ${tear_down()}
output:
- user_agent
- test:
name: 第一次登录
api: api/login.yml
extract:
token: content.content.token
validate:
- eq: ["status_code", 200]
output:
- token
- test:
name: 第二次登录
api: api/login.yml
setup_hooks:
- ${output_request($request)}
teardown_hooks:
- ${output_response($response)}
extract:
token: content.content.token
validate:
- eq: ["status_code", 200]
# output无效
output:
- token
执行看结果,控制台
3.2.4、工程结构
以上涉及的文件在工程结构是:api目录、debugtalk、testcase必须在同级目录;
而在hrun使用手脚架创建的工程结构是分api、testcases、testsuites、.env等,本身独立的api/xxx.yml是可以单独运行的,如何执行testcases层调用到api层呢非得进入testcases才能到且在当前目录有api层?但是为什么testsuites层能调到testcases层再到api层呢?
那么结论就是,在使用共同调用login获取token的testcases,其实是放在testsuite中调用api,然后在传给testcases? 这是错误的,错误的!!!
综上:httprunner框架通过–startproject创建的工程结构是支持api、testcases、testsuites同级调用的,也就是说需要在/根路径<工程>执行testsuites、testcases、api下的yml测试用例文件。
四、httprunner扩展
有时在debugtalk.py中也会使用框架的api类来执行测试用例
- 执行测试用例,可以是目录也可以是文件,也可以是目录和用例的混合:[“path”,“test_file”]
from httprunner.api import HttpRunner
runner = HttpRunner(
failfast=True,
save_tests=True,
log_level="INFO",
log_file="test.log"
)
summary = runner.run(path_or_tests)
- 也可以通过调用api生成测试报告,因为响应结果中有html报告中用到的元素
from httprunner import report
report_path = report.gen_html_report(
summary,
report_template="/path/to/custom_report_template",
report_dir="/path/to/reports_dir",
report_file="/path/to/report_file_path"
)