httprunner3.x的简介及使用
HttpRunner简介
HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求
相关文档
有必要先看一下httprunner2.x文档,了解一下httprunner的演进过程
安装
支持python 3.6+
pip3 install httprunner
- 升级
pip3 install -U httprunner
- 校验
$ httprunner -V
3.1.4
常用命令
- 使用-h或--help查看命令的使用方式
$ httprunner -h
usage: httprunner [-h] [-V] {run,startproject,har2case,make} ...
One-stop solution for HTTP(S) testing.
positional arguments:
{run,startproject,har2case,make}
sub-command help
run Make HttpRunner testcases and run with pytest.
startproject Create a new project with template structure.
har2case Convert HAR(HTTP Archive) to YAML/JSON testcases for
HttpRunner.
make Convert YAML/JSON testcases to pytest cases.
optional arguments:
-h, --help show this help message and exit
-V, --version show version
关于位置参数的命令的进一步使用方式仍可使用-h查看,如
httprunner run -h
- httprunner startproject
- httprunner run:等价于hrun
- har2case:也可使用httprunner har2case
快速开始
官方参考示例
- 生成脚手架
httprunner startproject demo
- 运行用例
hrun demo
或httprunner run demo
脚手架
- 先来看下脚手架目录结构
$ tree demo
demo # 项目目录
├── debugtalk.py # (可选)项目中可能需要的逻辑运算辅助函数等
├── har # 抓包导出的har文件
├── reports # 测试报告
└── testcases # 测试用例
├── demo_testcase_ref.yml # 支持yaml或json或py文件
└── demo_testcase_request.yml
这只是演示示例的一个简单结构,实际项目中可以使用
httprunner startproject
创建脚手架,然后根据项目需要添加其他目录,如数据驱动data目录来存放csv文件,.env文件存储项目环境变量或敏感信息,testsuites目录用于测试用例集存放等
- 一个完整的项目目录
demo
├── .env
├── data
│ ├── account.csv
│ └── vip.csv
├── debugtalk.py
├── har
├── logs
│ └── 2aad97e8-2c00-46a6-8897-e129e0d741be.run.log
├── reports
│ ├── assets
│ │ └── style.css
│ └── demo.html
├── testcases
│ ├── __init__.py
│ ├── demo_testcase_request.yml
│ ├── demo_testcase_request_test.py
└── testsuites
└── suite.yml
用例生成
抓包并导出har文件
- har文件夹用于存放抓包导出的har文件
- 使用fiddler或charles抓包,并选择需要的session导出为.har文件放于har文件夹
用例生成命令har2case
- 抓取
http://httpbin.org/get
并导出放于har文件夹如命名为httpbin_get.har - 执行如下命令
$ har2case har/httpbin_get.har
则在har文件夹中生成如下测试用例httpbin_get_test.py
# NOTE: Generated By HttpRunner v3.1.4
# FROM: har/httpbin_get.har
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
class TestCaseHttpbinGet(HttpRunner):
config = Config("testcase description").verify(False)
teststeps = [
Step(
RunRequest("/get")
.get("http://httpbin.org/get")
.with_headers(
**{
"Host": "httpbin.org",
"Cache-Control": "max-age=0",
"DNT": "1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Connection": "keep-alive",
}
)
.validate()
.assert_equal("status_code", 200)
.assert_equal("body.origin", "218.82.206.141")
.assert_equal("body.url", "http://httpbin.org/get")
),
]
if __name__ == "__main__":
TestCaseHttpbinGet().test_start()
用例生成的补充说明
- HttpRunner v3.x 支持3种用例格式:pytest、YAML和JSON
- HttpRunner 3.x中har2case命令默认生成pytest用例(如上示例,即不添加任何参数),这种用例的编写更方便使用IDE的补全(链式调用和语法提示),编写起来更方便,推荐使用
- 同样也可以使用YAML/JSON格式的用例(添加参数)使用如下命令,不推荐
har2case har/httpbin_get.har -2y # 或--to-yml
har2case har/httpbin_get.har -2j # 或--to-json
- YAML/JSON格式用例和pytest用例其实是等价的,使用
httprunner make
或hmake
命令转换成pytest用例
httprunner make har/httpbin_get.yml
httprunner make har/httpbin_get.json
用例分层及用例编写
用例分层
- 每个测试步骤teststep对应一个API请求或其他测试用例的引用,描述单次接口测试的全部内容,包括发起接口请求、解析响应结果、检验结果等
- 每个测试用例testcase都有1个或多个测试步骤teststep,测试用例是测试步骤的有序集合,测试用例应该是完整且独立的,每条测试用例应该是都可以独立运行的
- 测试用例集testsuit是测试用例testcase的无序集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的
测试用例
这里仅介绍pytest用例的编写。每一个测试用例都是HttpRunner的子类,必须包含两个属性config及teststeps
# HttpRunner部分源码
class HttpRunner(object):
config: Config
teststeps: List[Step]
......
config: Config
# Config部分源码
class Config(object):
def __init__(self, name: Text):
self.__name = name
self.__variables = {}
self.__base_url = ""
self.__verify = False
self.__export = []
self.__weight = 1
caller_frame = inspect.stack()[1]
self.__path = caller_frame.filename
......
- name:测试用例的名称,将在log和报告中展示
- base_url(可选):如果base_url被指定,测试步骤中的url只能写相对路径。当你要在不同环境下测试时,这个配置非常有用
- variables(可选):定义的全局变量,作用域为整个用例。每个测试步骤都可以引用config variables。但step variables 优先级高于 config variables
- verify(可选):指定是否验证服务器的TLS证书
- export(可选的):指定输出的测试用例变量,当一个测试用例在另一个测试用例的步骤中被引用时,config export将被提取并在随后的测试步骤中使用
teststeps: List[Step]
# Step部分源码
class Step(object):
def __init__(
self,
step_context: Union[
StepRequestValidation,
StepRequestExtraction,
RequestWithOptionalArgs,
RunTestCase, #
StepRefCase, #
],
):
self.__step_context = step_context.perform()
-
RunRequest(name)
在测试步骤step中对api发起请求,并对响应做一些校验或提取- name:用来定义测试步骤 name,将出现在log和测试报告中。
- .with_variables:测试步骤中定义的变量,作用域为当前测试步骤;测试步骤中的变量,会覆盖config variables中的同名变量
- .method(url):设置http方法,与requests.request 中的method和url参数对应;如果在config中设置了base_url,url只需要相对路径path
- .with_params:设置请求的query string,与requests.request中的params参数对应
- .with_headers:设置请求的headers,与requests.request中的headers参数对应
- .with_cookies:设置请求的cookies,与requests.request中的cookies参数对应
- .with_data:设置请求的body,与requests.request中的data参数对应
- .with_json:设置请求的json格式body,与requests.request中的json参数对应
关于requests相关参数可参考我之前的文章Python Requests 库的使用或官方文档
- extract:从请求的响应结果中提取参数,并保存到参数变量中(例如登陆账号后的用户token),后续测试用例可通过
$token
的形式进行引用with_jmespath(jmes_path: Text, var_name: Text)
:这里使用的是jmespath来提取json response body的内容,关于jmespath的使用参看之前的文章jemespath的使用或官方文档;var_name存储提取出来的值,可用于之后的测试步骤
- validate:测试用例中定义的结果校验项,作用域为当前测试用例,用于实现对当前测试用例运行结果的校验
- assert_XXX(jmes_path: Text, expected_value: Any, message: Text = ""):同样使用jemespath来提取响应中需要校验的字段值,并与expected_value进行比较。message用于描述断言失败原因
- assert_xxx相关方法可参看官方api或源码(IDE补全查看)
-
RunTestCase(name)
在测试步骤中引用另一个测试用例- .with_variables:同RunRequest中的.with_variables
- .call:指定引用的测试用例的类(可调用对象)
- .export:从引用的测试用例中提取的变量,该变量在后面的test steps中可以引用
在测试用例和测试步骤层面还提供hook机制来定制更复杂的场景,这里不详细介绍,请参考官方文档
variables优先级
从上面的测试用例的编写说明中,可以在不同的位置配置变量。可以使用不同的变量名来避免混淆,也可以 使用相同的变量名(如果必须的话),则但相同的变量变量名遵循如下的优先级
- 在测试用例testcase中:
- step variables > extracted variables
- parameter variables > config variables
- extracted variables > parameter variables > config variables
- config variables 优先级最低
- 在测试用例集testsuit中:
testcase variables > export variables > testsuite config variables > 被引用的testcase config variables
debugtalk.py
- 每个项目有且仅有一个该文件
- 存放自定义python函数
在测试用例中引用
${func(*arges, **kwargs)}
.env
.env
文件,项目根目录- 用于敏感数据信息存放,存储采用
name=value
的格式
在测试用例中引用
${ENV(name)}
- 还可以在debugtalk.py 中通过 Python 内置的函数 os.environ 对环境变量进行引用,然后再实现处理逻辑,最后在用例中通过
${func(*arges, **kwargs)}
来引用
参数化数据驱动
- 可在测试用例testcase或测试用例集testsuite中使用参数化数据驱动
from httprunner import Parameters
print(Parameters)
------输出结果---------
<function parse_parameters at 0x1078a4790>
# Parameters 部分源码
from httprunner.parser import parse_parameters as Parameters
def parse_parameters(parameters: Dict,) -> List[Dict]:
""" parse parameters and generate cartesian product.
Args:
parameters (Dict) parameters: parameter name and value mapping
parameter value may be in three types:
(1) data list, e.g. ["iOS/10.1", "iOS/10.2", "iOS/10.3"]
(2) call built-in parameterize function, "${parameterize(account.csv)}"
(3) call custom function in debugtalk.py, "${gen_app_version()}"
Returns:
list: cartesian product list
Examples:
>>> parameters = {
"user_agent": ["iOS/10.1", "iOS/10.2", "iOS/10.3"],
"username-password": "${parameterize(account.csv)}",
"app_version": "${gen_app_version()}",
}
>>> parse_parameters(parameters)
"""
脚本中直接指定参数列表
- 适合参数列表比较小的情况
- Parameters函数参数为字典,且字典的value值为list
- 字典的key如果是多个具有关联性的参数,需要将其定义在一起,并采用短横线(-)进行链接
- 在测试用例或测试用例集中使用
$var_name
来引用
# 例1
from httprunner import Parameters
print(Parameters({"user_agent": ["iOS/10.1", "iOS/10.2", "iOS/10.3"]}))
------输出结果-------
[{'user_agent': 'iOS/10.1'}, {'user_agent': 'iOS/10.2'}, {'user_agent': 'iOS/10.3'}]
# 例2
from httprunner import Parameters
print(Parameters({"username-password": [['jerry', '12345a'], ['tom', '12345b']]}))
-------输出结果--------
[{'username': 'jerry', 'password': '12345a'}, {'username': 'tom', 'password': '12345b'}]
引用CSV文件
- 适合数据量比较大的情况
- Parameters函数参数为字典,value值为
${parameterize(path/to/csv_file)}
- Parameters函数参数字典的key值(多个用短横线(-)分割)必须包含在CSV 文件中第一行的参数名称中,顺序可以不一致,参数个数也可以不一致
在data/account.csv中输入内容如下:
username,password
jerry,12345a
tom,12345b
CSV 文件中的第一行必须为参数名称,从第二行开始为参数值,每个(组)值占一行;具有多个参数,则参数名称和数值的间隔符需实用英文逗号
from httprunner import Parameters
print(Parameters({"username-password": "${parameterize(data/account.csv)}"}))
---------输出结果---------
[{'username': 'jerry', 'password': '12345a'}, {'username': 'tom', 'password': '12345b'}]
在debugtalk.py中自定义的函数生成参数列表
- 该种方式最为灵活,可通过自定义 Python 函数实现任意场景的数据驱动机制,也可用于动态生成参数列表
- 在自定义函数中,生成的参数列表必须为 list of dict 的数据结构,该设计主要是为了与 CSV 文件的处理机制保持一致
- Parameters函数的字典参数key值(多个则用短横线(-)分割)必须与自定义函数返回的list中的dict key值相呼应(与CSV 文件的处理机制保持一致)
在debugtalk.py定义如下函数
def get_user_id():
return [
{"user_id": 1001},
{"user_id": 1002},
{"user_id": 1003},
{"user_id": 1004}
]
from httprunner import Parameters
print(Parameters({"user_id": "${get_user_id()}"}))
--------输出结果--------
[{'user_id': 1001}, {'user_id': 1002}, {'user_id': 1003}, {'user_id': 1004}]
在测试用例中使用参数化
在官方文档中没有详细的说明文档,可参看github中的的例子github examples
- 使用@pytest.mark.parametrize实现
# NOTE: Generated By HttpRunner v3.1.4
# FROM: request_methods/request_with_parameters.yml
import pytest
from httprunner import Parameters
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
class TestCaseRequestWithParameters(HttpRunner):
@pytest.mark.parametrize(
"param",
Parameters(
{
"user_agent": ["iOS/10.1", "iOS/10.2"],
"username-password": "${parameterize(request_methods/account.csv)}",
"app_version": "${get_app_version()}",
}
),
)
def test_start(self, param):
super().test_start(param)
config = (
Config("request methods testcase: validate with parameters")
.variables(**{"app_version": "f1"})
.base_url("https://postman-echo.com")
.verify(False)
)
teststeps = [
Step(
RunRequest("get with params")
.with_variables(
**{
"foo1": "$username",
"foo2": "$password",
"sum_v": "${sum_two(1, $app_version)}",
}
)
.get("/get")
.with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
.with_headers(**{"User-Agent": "$user_agent,$app_version"})
.extract()
.with_jmespath("body.args.foo2", "session_foo2")
.validate()
.assert_equal("status_code", 200)
.assert_string_equals("body.args.sum_v", "${sum_two(1, $app_version)}")
),
]
if __name__ == "__main__":
TestCaseRequestWithParameters().test_start()
运行测试用例
- 运行指定路径的用例:
hrun path/to/testcase1
- 指定多个测试用例路径执行:
hrun path/to/testcase1 path/to/testcase2
- 运行测试项目中所有测试用例(指定测试项目的绝对路径):
hrun path/to/testcase_folder/
- hrun包装了pytest,因此pytest的功能都可以在hrun中使用
$ hrun -h # 查看帮助文档及详细的参数说明
测试报告reports
这里仅介绍allure测试报告,内置html报告请参考官方文档
allure测试报告
- 安装allure-pytest,使用
pip install allure-pytest
- 安装好allure-pytest后就可以在运行用例时添加如下参数
--alluredir=DIR
: 指定目录生成报告--clean-alluredir
: 清理alluredir文件夹--allure-no-capture
: 不将pytest捕获的日志记录/stdout/stderr附加到测试报告
更多使用方式可参看之前的文章pytest 生成测试报告或官方文档allure-pytest