今天来介绍一个python的一个开源项目:httprunner,接口自动化工具。第一次输入,难免有不周到的地方,轻喷~

介绍:

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文件

image.png
  • 编辑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”(因为我们是执行用例)

image.png
  • 进入main(),因为我们含有“run”,所以现在开始执行用例。

image.png
  • 进入main_run(extra_args),根据extra_args(yml/json路径)将yml/json文件生成python文件(测试文件)

image.png
  • 进入main_make(),判断路径是否存在

image.png
  • 进入 __make() ,加载yml/json文件,并准备数据(按照数据的优先级进行复制,从低到高)

image.png
  • 进入make_testcase(),将yml/json文件基于模板生成对应的py文件

image.png
  • 最后回到main_run()方法,继续执行main_make()以下的代码,可以发现,程序最终将创建的py文件交给pytest.main()来执行

image.png
  • 上面演示了用例文件从yml/json到py文件的生成流程。接下来可以对生成的py文件继续调试。
  • 这里我做了一个比较简单的演示,小伙伴们如果有兴趣,可以根据我的思路继续你的调试之旅哦。

用例管理

关键概念:

概括来说,测试用例分层机制的核心是将接口定义、测试步骤、测试用例、测试场景进行分离,单独进行描述和维护,从而尽可能地减少自动化测试用例的维护成本。

  • 测试用例(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定义为只有一个请求步骤的测试用例**(需要重点注意一下这里)**。

测试用例分层模型


image.png
  • 去掉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函数」

  • hook 函数的定义放置在项目的 debugtalk.py 中,在 YAML/JSON 中调用 hook 函数仍然是采用 ${func(b)} 的形式。
  • 可传自定义参数:对于测试用例层面的 hook 函数,与 YAML/JSON 中自定义的函数完全相同,可通过自定义参数传参的形式来实现灵活应用。
  • 可传request与response参数:对于单个测试用例层面的 hook 函数,除了可传入自定义参数外,还可以传入与当前测试用例相关的信息,包括请求的 response(对于requests.Response,也就是响应体),用于实现更复杂场景的灵活应用。如`${print_req($request)} `。
# ${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 实现参数化数据驱动机制:https://debugtalk.com/post/httprunner-data-driven/
HttpRunner 再议参数化数据驱动机制:https://debugtalk.com/post/httprunner-data-driven-refactor/

使用

需要注意的是,从v2.0开始,参数化只支持在 testsuite 中实现。不再支持在测试用例文件中进行参数化配置。

「参数配置概述」

  • 独立参数:指的是不与其他参数有任何关联关系,与其他参数是相互独立的。
  • 关联参数:假如测试用例中定义了多个参数,那么测试用例在运行时会对参数进行笛卡尔积组合,覆盖所有参数组合情况。

「具体使用」
https://v2.httprunner.org/prepare/parameters/

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文件)。大家可以对比学习下~。

接口自动化测试之httprunner初探的更多相关文章

  1. 接口自动化 Windows + HttpRunner 初探(一)

    运行环境 HttpRunner 是一个基于 Python 开发的测试框架,可以运行在 macOS.Linux.Windows 系统平台上. HttpRunner 的开发环境为 macOS + Pyth ...

  2. 接口自动化框架 - httprunner

    自己曾经写过一个接口自动化的框架,并做了一版本的优化,一直觉得做的还不错,且没依赖现有的框架. 最近因为一些工作的原因,开始又思考之前写的框架的一些缺点及如何优化,所以找到比较有名的httprunne ...

  3. postman接口自动化测试之如何使用)

    postman 是一款强大网页调试工具的客户端,postman为用户提供强大的 Web API & HTTP 请求调试功能.postman能够发送任何类型的HTTP 请求 (GET, HEAD ...

  4. python WEB接口自动化测试之requests库详解

    由于web接口自动化测试需要用到python的第三方库--requests库,运用requests库可以模拟发送http请求,再结合unittest测试框架,就能完成web接口自动化测试. 所以笔者今 ...

  5. 【转】JAVA接口自动化测试之一个测试方法对应多条测试数据的实现方式

    一.痛点:一条测试数据对应一个测试方法 前面的章节中我们已经写代码实现了登录接口的处理调用,但是一个接口往往是需要多条测试用例才能完整的覆盖到每一种情况. 针对于单接口多条测试用例需要执行的情况,该如 ...

  6. postman接口自动化测试之添加Tests检查点

    一.概念 Postman的Tests本质上是JavaScript代码,通过我们编写测试代码,每一个Tests返回True,或是False,以判断接口返回的正确性. 其实,每一个Tests实际上就是一个 ...

  7. Java接口自动化测试之集成MyBatis和MySQL (五)

    pom.xml新增dependency <dependency> <groupId>org.mybatis</groupId> <artifactId> ...

  8. Java接口自动化测试之TestNG测试报告ExtentReports的应用(三)

    pom.xml导入包 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=" ...

  9. (转)接口自动化测试之http请求实践总结

    一.接口测试的基本思路 1.确定要测试接口的请求类型.接口是get请求还是post请求. 2.确定接口的参数.需要传输的参数有哪些,类型分别是什么,都有哪些要求等. 3.按照参数要求构造请求需要的参数 ...

随机推荐

  1. 9.11、mysql增量备份和增量恢复介绍

    1.增量备份: 增量数据是从上次全量备份之后,更新的新数据,对于mysql来说,binlog日志就是mysql的增量数据: (1)按天进行备份: 周一00点全量备份 周二00点全量备份 ...... ...

  2. 一、.Net Core 依赖注入详解及Autofac使用

    .NET中的依赖注入实际上帮助我们解耦了我们的代码,是控制反转和依赖反转原则的具体实现. .Net Core的依赖注入的好处: 1. application 更稳定,容易维护和演化: 2. 实现细节的 ...

  3. 《手把手教你》系列技巧篇(七)-java+ selenium自动化测试-宏哥带你全方位吊打Chrome启动过程(详细教程)

    1.简介 经过前边几篇文章和宏哥一起的学习,想必你已经知道了如何去查看Selenium相关接口或者方法.一般来说我们绝大多数看到的是已经封装好的接口,在查看接口源码的时候,你可以看到这个接口上边的注释 ...

  4. vue elementui table 内按钮跳转页面

    vue : <el-table-column label="操作" v-if="isColumOperate"> <template slot ...

  5. AcWing 903. 昂贵的聘礼

    年轻的探险家来到了一个印第安部落里. 在那里他和酋长的女儿相爱了,于是便向酋长去求亲. 酋长要他用10000个金币作为聘礼才答应把女儿嫁给他. 探险家拿不出这么多金币,便请求酋长降低要求. 酋长说:& ...

  6. ExtJs4学习(八)数据代理Proxy

    ExtJs数据代理我们介绍常用的四种,但会着重介绍ajax代理,因为日常开发中,这个最为常用 Ext.data.proxy.Ajax AjaxProxy(Ajax数据代理类)是你的应用程序中使用最广泛 ...

  7. python 字符串 增、删、改、查基本操作

    private static String TAG = "MainActivity"; private String str = " a,bB,1cCcc,2dDd d2 ...

  8. mysql中函数cast使用

    CAST函数语法规则是:Cast(字段名 as 转换的类型 ),其中类型可以为: CHAR[(N)] 字符型DATE 日期型DATETIME 日期和时间型DECIMAL float型SIGNED in ...

  9. AcWing 1086. 恨7不成妻(【代码简洁】标准记忆化搜索+超详解!!)

    看到这题用循环写的dp代码瑟瑟发抖~ 数位dp一般记忆化搜索的写法思维难度较低,也比较常用,这题的简洁代码应该就可以显现出其优越性 (用时4ms,可能比用循环写的dp还要快) 那这里补充一下记忆化搜索 ...

  10. 常见最基础的Dos命令.

    打开cmd的方式. 1.+系统+命令提示符 2.Win+R 输入cmd 打开命令台 (推荐使用) 3.在任意的文件夹下按住SHIFT 加鼠标右键 在此处打开命令行窗口 4.资源管理器的地址栏前面加上 ...