介绍:
HttpRunner是一个简单优雅但功能强大的 HTTP(S) 测试框架。以YAML或JSON格式定义测试用例,保障测试用例描述的统一性和可维护性。程序执行的时候,会处理用户输入的yml/json文件并基于模板生成测试文件。最终通过pytest.main([])的方式去执行生成的用例文件。用户只需要通过json/yml文件去维护用例即可,不需要关心程序如何处理json/yml文件,如何生成测试文件等,简单快速通过pytest运行用例,并获取详细的测试报告。
主要特征:
- 以YAML或JSON格式定义测试用例,保障测试用例描述的统一性和可维护性
- testsuite > testcase > teststep(api)
- 支持设计一系列的测试场景,每个测试场景可包含多个teststep。
- 支持参数化设计
- 支持variables/ extract/ validate/hooks机制(使用jmespath,提取和验证json响应)
- 支持添加逻辑运算辅助函数(debugtalk.py),在测试脚本中实现复杂的动态逻辑
- 在 HAR 支持下记录并生成测试用例。(使用charles去抓取请求,生成的用例文件可能还需要手动处理)
- 使用 pytest 执行测试文件 ,数百个插件随时可用。使用 allure,测试报告可以非常强大。
- run_testcase():处理请求前的数据
- __run_step() > __run_step_request(使用requests发起api请求并导出其他用例引用的变量(__step_datas))
- 通过重复使用locust ,您可以进行性能测试,而无需进行额外的工作。
- 支持CLI命令,与CI/CD完美结合。
工作流程
想要了解其工作流程,最好的办法就是使用debug模式,那在这里应该如何使用debug来参透httprunner的执行流程呢?跟着我来看下:
调试技巧
使用pycharm进行调试,且python环境使用Virtualenv进行管理
- 安装:pip install httprunner
- 在
/venv/bin/
目录下找到hrun文件
- 编辑hrun文件,为sys.argv赋值。第一个是hrun的绝对路径,第二个是用例的绝对路径
- 在
sys.exit(main_hrun_alias())
这里打个断点,那么就可以开始愉快的调试之旅了~
-
# -*- coding: utf-8 -*-
-
import re
-
import sys
-
from httprunner.cli import main_hrun_alias
-
if __name__ == '__main__':
-
sys.argv = ['/Users/boyizhang/PycharmProjects/apitest/venv/bin/hrun', '/Users/boyizhang/PycharmProjects/apitest/hruntests/testcases/testheader.yml']
-
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
-
sys.exit(main_hrun_alias())
工作流
调试日志:
- 进入main_hrun_alias()中,并为sys.argv的列表插入新的一项“run”(因为我们是执行用例)
进入main(),因为我们含有“run”,所以现在开始执行用例。
进入main_run(extra_args),根据extra_args(yml/json路径)将yml/json文件生成python文件(测试文件)
- 进入main_make(),判断路径是否存在
- 进入 __make() ,加载yml/json文件,并准备数据(按照数据的优先级进行复制,从低到高)
进入make_testcase(),将yml/json文件基于模板生成对应的py文件
最后回到main_run()方法,继续执行main_make()以下的代码,可以发现,程序最终将创建的py文件交给pytest.main()来执行
- 上面演示了用例文件从yml/json到py文件的生成流程。接下来可以对生成的py文件继续调试。
- 这里我做了一个比较简单的演示,小伙伴们如果有兴趣,可以根据我的思路继续你的调试之旅哦。
-
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
-
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
-
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
-
分享他们的经验,还会分享很多直播讲座和技术沙龙
-
可以免费学习!划重点!开源的!!!
-
qq群号:455787643【暗号:csdn11】
用例管理
关键概念:
概括来说,测试用例分层机制的核心是将接口定义、测试步骤、测试用例、测试场景进行分离,单独进行描述和维护,从而尽可能地减少自动化测试用例的维护成本。
- 测试用例(testcase)应该是完整且独立的,每条测试用例应该是都可以独立运行的
- 测试用例(测试场景)是测试步骤(teststep)的 **有序 **集合,每一个测试步骤对应一个 API 的请求描述
- 测试用例集(testsuite)是测试用例的 无序 集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的;如果确实存在先后依赖关系,那就需要在测试用例中完成依赖的处理
- 多个测试场景组成一个测试套件,运行一个测试套件,可以同时执行多个测试场景。
- 接口定义(api definition):为了更好地对接口描述进行管理,使用独立的文件对接口描述进行存储,即每个文件对应一个接口描述。
V2.0
建议大家用最新版本的,但是有兴趣了解httprunner的架构变化的同学,可以继续读V2.0模块的内容。
测试用例分层模型
V2.0的版本中,存在api definition的概念。
- 所有的api都置于api文件夹,每个api创建一个yml/json文件进行管理
- testcase中的teststep引用对应的api
优点是:api管理方便;缺点是:在testcase中的teststep可以直接引用api definition,不管testcase是单个步骤的简单场景还是多个步骤的复杂场景,都需要进行引用api definition。这样的话,对于单个步骤的简单场景而言,又和api definition很相似,这样就会造成重复描述,而且容易混淆。 **
那么,我们如何在testcase>teststep中引用api呢?如何引用testcase呢? **
V2.0案例学习
在teststep中可以通过api字段引用api definition 、通过testcase字段引用testcase。下面我们根据脚手架快速创建一个项目httprunner --startproject hruntest2.0
(需要确保安装的是httpruner3.0以下的版本)来说明:
-
$ httprunner --startproject hruntest2.0 && tree hruntest2.0
-
Start to create new project: hruntest2.0
-
CWD: /Users/boyizhang/PycharmProjects/apitest
-
created folder: hruntest2.0
-
created folder: hruntest2.0/api
-
created folder: hruntest2.0/testcases
-
created folder: hruntest2.0/testsuites
-
created folder: hruntest2.0/reports
-
created file: hruntest2.0/api/demo_api.yml
-
created file: hruntest2.0/testcases/demo_testcase.yml
-
created file: hruntest2.0/testsuites/demo_testsuite.yml
-
created file: hruntest2.0/debugtalk.py
-
created file: hruntest2.0/.env
-
created file: hruntest2.0/.gitignore
-
hruntest2.0
-
├── api
-
│ └── demo_api.yml
-
├── debugtalk.py
-
├── reports
-
├── testcases
-
│ └── demo_testcase.yml
-
└── testsuites
-
└── demo_testsuite.yml
- api definition
-
# api/demo_api.yml
-
name: demo api
-
variables:
-
var1: value1
-
var2: value2
-
request:
-
url: /api/path/$var1
-
method: POST
-
headers:
-
Content-Type: "application/json"
-
json:
-
key: $var2
-
validate:
-
- eq: ["status_code", 200]
- testcase
-
config:
-
name: "demo testcase"
-
variables:
-
device_sn: "ABC"
-
username: ${ENV(USERNAME)}
-
password: ${ENV(PASSWORD)}
-
base_url: "http://127.0.0.1:5000"
-
teststeps:
-
-
-
name: demo step 1
-
api: path/to/api1.yml
-
variables:
-
user_agent: 'iOS/10.3'
-
device_sn: $device_sn
-
extract:
-
- token: content.token
-
validate:
-
- eq: ["status_code", 200]
在teststep中使用api选项来引用api definition,执行testcase/demo_testcase.yml用例文件。(注:api definition不属于testcase范畴,无法直接执行api/下的yml/json文件)。在teststep中传的字段的优先级会比api definition下的高,也就是说,如果teststep传某个字段,那么会有限使用teststep传的那个,如果没有,则再使用api definition的字段。
V3.0
为了简单,在HttpRunner v2.x中的API概念已经被取消了。可以将API定义为只有一个请求步骤的测试用例(需要重点注意一下这里)。
测试用例分层模型
- 去掉api的概念,把api的概念转为测试用例的概念。将API定义为只有一个请求步骤的测试用例。以统一的概念:测试用例,使得维护用例的工作更加方便。
- 按照V2.0的逻辑,需要维护api definition,也需要维护只有一个测试步骤的testcase,而V3.0中,我们只需要维护testcase即可。v2->v3的转变,使得可以在避免重复描述的同时,解决测试用例的依赖关系,从而保证每个测试用例都是独立可运行的。
那么,问题来了,我们应该如何在某个testcase的teststep中引用其他testcase呢?
v3.0案例学习
在测试步骤(teststep)中,可通过 testcase 字段引用其它测试用例,引用方式为对应测试用例文件的路径,绝对路径或相对路径均可。推荐使用相对路径,路径基准为项目根目录,即 debugtalk.py 所在的目录路径。
**通过 **httprunner startproject hruntest3.0
快速创建项目。
-
$ tree hruntest3.0
-
hruntest3.0
-
├── debugtalk.py
-
├── har
-
├── reports
-
└── testcases
-
├── demo_testcase_ref.yml
-
└── demo_testcase_request.yml
-
# demo_testcase_request.yml
-
config:
-
name: "request methods testcase with functions"
-
variables:
-
foo1: config_bar1
-
foo2: config_bar2
-
expect_foo1: config_bar1
-
expect_foo2: config_bar2
-
base_url: "https://postman-echo.com"
-
verify: False
-
export: ["foo3"]
-
teststeps:
-
-
-
name: post form data
-
variables:
-
foo2: bar23
-
request:
-
method: POST
-
url: /post
-
headers:
-
User-Agent: HttpRunner/${get_httprunner_version()}
-
Content-Type: "application/x-www-form-urlencoded"
-
data: "foo1=$foo1&foo2=$foo2&foo3=$foo3"
-
validate:
-
- eq: ["status_code", 200]
-
- eq: ["body.form.foo1", "$expect_foo1"]
-
- eq: ["body.form.foo2", "bar23"]
-
- eq: ["body.form.foo3", "bar21"]
-
# demo_testcase_ref.yml
-
config:
-
name: "request methods testcase: reference testcase"
-
variables:
-
foo1: testsuite_config_bar1
-
expect_foo1: testsuite_config_bar1
-
expect_foo2: config_bar2
-
base_url: "https://postman-echo.com"
-
verify: False
-
teststeps:
-
-
-
name: request with functions
-
variables:
-
foo1: testcase_ref_bar1
-
expect_foo1: testcase_ref_bar1
-
testcase: testcases/demo_testcase_request.yml
-
export:
-
- foo3
-
-
-
name: post form data
-
variables:
-
foo1: bar1
-
request:
-
method: POST
-
url: /post
-
headers:
-
User-Agent: HttpRunner/${get_httprunner_version()}
-
Content-Type: "application/x-www-form-urlencoded"
-
data: "foo1=$foo1&foo2=$foo3"
-
validate:
-
- eq: ["status_code", 200]
-
- eq: ["body.form.foo1", "bar1"]
-
- eq: ["body.form.foo2", "bar21"]
在teststep中使用testcase字段来引用,执行demo_testcase_ref.yml用例文件,当执行到name = request with functions这个步骤的时候会先执行其引用的testcase:testcases/demo_testcase_request.yml
。 在根目录下,执行hrun testcases/demo_testcase_ref.yml
,可以看到,程序也生成了该case:testcases/demo_testcase_request.yml
的测试文件:
hook机制
涉及hook处理
背景
在自动化测试中,执行用例前,需要执行一些预处理操作,执行用例后,需要做一些清理工作。如果手动去操作的话,就不是很合适。所以就需要用到hook机制,在执行用例前后执行hook函数。
使用
hook 机制分为两个层级:
- 测试用例层面(testcase)
- 在测试用例层面,主要在config字段新增两个关键字 setup_hooks 和 teardown_hooks。在这里,其主要目的就是用于测试前的准备工作以及测试后的清理工作。
- 测试步骤层面(teststep)
- 在每个teststep中新增关键字 setup_hooks 和 teardown_hooks。实现对请求的 request 内容进行预处理以及实现对响应的 response 进行修改。
编写hook函数
-
# ${print_request($request)}
-
2021-07-18 09:30:46.740 | DEBUG | httprunner.runner:__call_hooks:121 - call hook function: ${print_reqeust($request)}
-
{'method': 'GET', 'url': '/get', 'params': {'foo1': 'bar11', 'foo2': 'bar21', 'sum_v': 3}, 'headers': {'User-Agent': 'HttpRunner/3.1.5', 'HRUN-Request-ID': 'HRUN-7768261f-0abf-4ce5-abf2-06327de85fd7-846739'}, 'req_json': None, 'data': None, 'cookies': {}, 'timeout': 120, 'allow_redirects': True, 'verify': False}
-
# ${print_req($response)}
-
2021-07-18 09:36:03.019 | DEBUG | httprunner.runner:__call_hooks:121 - call hook function: ${print_req($response)}
-
<httprunner.response.ResponseObject object at 0x109c87e50>
环境(.env)
涉及env的处理,文档
-
# parser.py
-
def get_mapping_function(
-
function_name: Text, functions_mapping: FunctionsMapping
-
) -> Callable:
-
#省略
-
if function_name in functions_mapping:
-
return functions_mapping[function_name]
-
elif function_name in ["environ", "ENV"]:
-
return utils.get_os_environ
-
#省略
-
raise exceptions.FunctionNotFound(f"{function_name} is not found.")
-
# utils.py
-
def set_os_environ(variables_mapping):
-
""" set variables mapping to os.environ
-
"""
-
for variable in variables_mapping:
-
os.environ[variable] = variables_mapping[variable]
-
logger.debug(f"Set OS environment variable: {variable}")
-
def get_os_environ(variable_name):
-
try:
-
return os.environ[variable_name]
-
except KeyError:
-
raise exceptions.EnvNotFound(variable_name)
加载用例之前,会先把.env文件中的变化加载到环境中,如果用例用含有ENV或者environ,则通过os.environ
去读取相应的值。
参数化:
实现
HttpRunner 实现参数化数据驱动机制:debugtalk.com/post/httpru…
HttpRunner 再议参数化数据驱动机制:debugtalk.com/post/httpru…
使用
需要注意的是,从v2.0开始,参数化只支持在 testsuite 中实现。不再支持在测试用例文件中进行参数化配置。
参数配置概述
- 独立参数:指的是不与其他参数有任何关联关系,与其他参数是相互独立的。
- 关联参数:假如测试用例中定义了多个参数,那么测试用例在运行时会对参数进行笛卡尔积组合,覆盖所有参数组合情况。
extract/export
底层使用 jmespath,提取和验证json响应,使得提取更简单。 extract用来抓取响应体中的字段,export导出当前用例抓取的字段,供引用该用例的用例使用。
测试报告
- pytest内置报告
- hrun /path/to/testcase --html=report.html
- allure报告
- pip install allure-pytest
- 执行:hrun /path/to/testcase --alluredir=/tmp/my_allure_results
- 在线打开报告:allure serve /tmp/my_allure_results 或者 生成html报告:allure generate reports/allure -o reports/allure/html
好好利用httprunner,可以覆盖80%的场景,可以说是一个不错的工具,当然我们除了学习应该如何使用之外,更应该学习人家设计的思想,这样自己之后才能有机会做好一点的工具。 另外推荐一个Java版本的接口自动化工具大家可以参考rest-assured
。httprunner与rest-assured
实现方式大同小异,不过 rest-assured
并没有通过json/yaml去管理用例,而是直接写测试文件(类似httprunner生成的py文件)。大家可以对比学习下~。
总结:
感谢每一个认真阅读我文章的人!!!
作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。
软件测试面试文档
我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。