HttpRunner V3.x 从入门到精通

一、介绍

HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份YAML/JSON脚本,即可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求。
官方文档:https://docs.httprunner.org/

二、框架设计理念

  • 充分复用优秀的开源项目,不追求重复造*,而是将强大的*组装成战车
  • 遵循 约定大于配置 的准则,在框架功能中融入自动化测试最佳工程实践
  • 追求投入产出比,一份投入即可实现多种测试需求

三、核心特点

  • 继承 Requests 的全部特性,轻松实现 HTTP(S) 的各种测试需求
  • 以YAML或JSON格式定义testcase,使用pytest运行,更加简洁优雅
  • 有了pytest,成百上千的插件随时可用
  • 支持variables/extract/validate/hooks机制来创建极其复杂的测试场景
  • 借助辅助函数(debugtalk.py),任何函数都可以在测试用例的任何部分中使用
  • 支持完善的测试用例分层机制,充分实现测试用例的复用
  • 使用python的第三方库jmespath,让提取和验证json响应更简单
  • 基于 HAR 实现接口录制和用例生成功能(har2case)
  • 结合 Locust 框架,无需额外的工作即可实现分布式性能测试
  • 执行方式采用 CLI 调用,可与 Jenkins 等持续集成工具完美结合
  • 集成了Allure,让测试报告更加漂亮,内容更丰富。
  • 极强的可扩展性,轻松实现二次开发和 Web 平台化

四、环境说明

HttpRunner 是一个基于 Python 开发的测试框架,可以运行在 macOS、Linux、Windows 系统平台上。笔者使用的是macOS系统,所以后续都是基于macOS系统的学习。

另外,HttpRunner 支持 Python 3.5 及以上的所有版本,虽然 HttpRunner 暂时保留了对 Python 2.7 的兼容支持,但强烈建议使用 Python 3.6 及以上版本。

电脑上还没安装Python的童鞋还请自行查询安装,非常的简单,这里附上python的官方地址:https://www.python.org/

五、安装HttpRunner

HttpRunner 的稳定版本托管在 PyPI 上,可以使用pip进行安装,非常的便捷。
打开命令行,输入安装命令即可:
二、安装HttpRunner
HttpRunner 的稳定版本托管在 PyPI 上,可以使用pip进行安装,非常的便捷。
打开CMD,输入安装命令即可:

pip install httprunner

如果已经安装过的,也可以进行更新升级一下。

pip install -U HttpRunner

检验是否安装成功
输入命令:

$ hrun -V
3.1.4

显示出版本号,说明安装成功。
你也可以通过输入:hrun -h,查看命令帮助说明。
在 HttpRunner 安装成功后,系统中会新增 4 个命令:

  • httprunner: 核心命令
  • hrun:httprunner 的缩写,功能与 httprunner 完全相同
  • hmakehttprunner make的别名,用于将YAML/JSON测试用例转换为pytest文件
  • har2case:辅助工具,可将标准通用的 HAR 格式(HTTP Archive)转换为YAML/JSON格式的测试用例

六、快速生成项目

环境装好了,相信很多童鞋已经迫不及待的想run起来了,但是面对一个陌生的框架又无从下手。没关系,我们可以用脚手架来快速生成一个httprunner项目。
我们不妨先输入httprunner startproject -h,来看一下命令说明。

chenshiengdeMBP:PythonCode chenshifeng$ httprunner startproject -h

usage: httprunner startproject [-h] [project_name]

positional arguments:
  project_name  Specify new project name.

optional arguments:
  -h, --help    show this help message and exit

可以看出,只需要在命令后面带上项目名称这个参数就好了,那就先来创建一个项目,名称叫httprunner_demo。

chenshiengdeMBP:PythonCode chenshifeng$ httprunner startproject httprunner_demo
2021-05-04 17:27:16.152 | INFO     | httprunner.scaffold:create_scaffold:43 - Create new project: httprunner_demo
Project Root Dir: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo

created folder: httprunner_demo
created folder: httprunner_demo/har
created folder: httprunner_demo/testcases
created folder: httprunner_demo/reports
created file: httprunner_demo/testcases/demo_testcase_request.yml
created file: httprunner_demo/testcases/demo_testcase_ref.yml
created file: httprunner_demo/debugtalk.py
created file: httprunner_demo/.env
created file: httprunner_demo/.gitignore

$ tree httprunner_demo -a
2021-05-04 17:27:16.161 | WARNING  | httprunner.scaffold:show_tree:29 - tree command not exists, ignore.
Sentry is attempting to send 0 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit
chenshiengdeMBP:PythonCode chenshifeng$ 

项目生成完毕,也是非常的简单。
如果你输入的项目名称已经存在,httprunner会给出warning提示。

chenshiengdeMBP:PythonCode chenshifeng$ httprunner startproject httprunner_demo
2021-05-04 17:29:00.631 | WARNING  | httprunner.scaffold:create_scaffold:32 - Project folder httprunner_demo exists, please specify a new project name.

$ tree httprunner_demo -a
2021-05-04 17:29:00.636 | WARNING  | httprunner.scaffold:show_tree:29 - tree command not exists, ignore.
Sentry is attempting to send 0 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit

相信了解过django的童鞋能感觉到,httprunner startproject这个命令跟django里的django-admin.py startproject project_name 很像,没错,其实httprunner的想法正式来源于django,这就是httprunner作为一个优秀开源技术资源整合和复用的体现之一,后续还有很多,届时提点出来。

七、项目结构梳理

我把生成出的项目用pycharm打开,可以看的生成的目录结构如下图,那么这些都是什么意思呢?
HttpRunner V3.x 从入门到精通

  • debugtalk.py 放置在项目根目录下(借鉴了pytest的conftest.py文件的设计)
  • .env 放置在项目根目录下,可以用于存放一些环境变量
  • reports 文件夹:存储 HTML 测试报告
  • testcases 用于存放测试用例
  • har 可以存放录制导出的.har文件
    具体用法会在后续中细讲,我们可以点开生成的testcases文件夹下的测试用例,里面是提供了一个可运行的demo内容的,那先来运行一下看看。
    HttpRunner V3.x 从入门到精通

运行用例:
hrun httprunner_demo

chenshiengdeMBP:PythonCode chenshifeng$ hrun httprunner_demo
2021-05-04 17:38:15.284 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo
2021-05-04 17:38:15.290 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-04 17:38:15.293 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/.env
2021-05-04 17:38:15.294 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2021-05-04 17:38:15.294 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2021-05-04 17:38:15.296 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_ref.yml
2021-05-04 17:38:15.306 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-04 17:38:15.306 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request.yml
2021-05-04 17:38:15.307 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request_test.py
2021-05-04 17:38:15.308 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_ref_test.py
2021-05-04 17:38:15.317 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-04 17:38:15.317 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request.yml
2021-05-04 17:38:15.317 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_ref_test.py
reformatted /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request_test.py
All done! ? ?? ?
2 files reformatted.
2021-05-04 17:38:16.063 | INFO     | httprunner.cli:main_run:56 - start to run tests with pytest. HttpRunner version: 3.1.4
============================================= test session starts ==============================================
platform darwin -- Python 3.9.2, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode
plugins: metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 2 items                                                                                              

httprunner_demo/testcases/demo_testcase_ref_test.py .                                                    [ 50%]
httprunner_demo/testcases/demo_testcase_request_test.py .                                                [100%]

============================================== 2 passed in 6.21s ===============================================

可以看的httprunner输出了运行过程中的调试信息。最后,运行结束,2个用例运行pass。

八、录制生成测试用例

在正式手动编写case之前,我们可以先来熟悉下httprunner的录制生成用例功能。
用postman的童鞋都知道,里面有个功能可以将接口转换成代码,可以直接copy过来使用,提升case编写效率。
那httprunner的录制生成用例功能又是怎么回事呢?

8.1、har2case

其实,这都要依托于另一个独立的项目-har2case。
原理就是当前主流的抓包工具和浏览器都支持将抓取得到的数据包导出为标准通用的 HAR 格式(HTTP Archive),然后 HttpRunner 将 HAR 格式的数据包转换为YAML/JSON格式的测试用例文件。
比如,我现在用macOS系统上的Charles去抓取一个百度首页的请求。
HttpRunner V3.x 从入门到精通
选中这个请求,点击左上角的File——Export Sessions——(可以选择导出选中的也可以导出所有),这里我们选择导出选中的,导出HTTPArchive,文件名baidu_home.har,保存到了项目的har目录下。
HttpRunner V3.x 从入门到精通
HttpRunner V3.x 从入门到精通

8.2、转换为pytest文件

运行命令将har文件转换成测试用例:

har2case baidu_home.har
chenshifengdeMacBook-Pro:har chenshifeng$ har2case baidu_home.har
2021-05-04 17:52:23.608 | INFO     | httprunner.ext.har2case.core:gen_testcase:356 - Start to generate testcase from /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/har/baidu_home.har
2021-05-04 17:52:23.608 | INFO     | httprunner.ext.har2case.core:_make_testcase:347 - Extract info from HAR file and prepare for testcase.
2021-05-04 17:52:23.611 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-04 17:52:23.611 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/.env
2021-05-04 17:52:23.612 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2021-05-04 17:52:23.612 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2021-05-04 17:52:23.612 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/har/baidu_home.har
2021-05-04 17:52:23.613 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/har/baidu_home_test.py
2021-05-04 17:52:23.613 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/har/baidu_home_test.py
All done! ? ?? ?
1 file reformatted.
2021-05-04 17:52:23.914 | INFO     | httprunner.ext.har2case.core:gen_testcase:377 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/har/baidu_home_test.py
Sentry is attempting to send 0 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit

生成完毕,在har目录下可以看到生成出的python文件:

# NOTE: Generated By HttpRunner v3.1.4
# FROM: har/baidu_home.har


from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase


class TestCaseBaiduHome(HttpRunner):

    config = Config("testcase description").verify(False)

    teststeps = [
        Step(
            RunRequest("/s")
            .get("https://www.baidu.com/s")
            .with_params(
                **{
                    "ie": "utf-8",
                    "mod": "1",
                    "isbd": "1",
                    "isid": "C9FF25725AB54698",
                    "f": "8",
                    "rsv_bp": "1",
                    "rsv_idx": "1",
                    "tn": "baidu",
                    "wd": "httprunner",
                    "fenlei": "256",
                    "oq": "httprunner%20%26lt%3B",
                    "rsv_pq": "86a39119000039fe",
                    "rsv_t": "9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ++oE9TlK6y2o+O7A7XdDS6Yus",
                    "rqlang": "cn",
                    "rsv_enter": "0",
                    "rsv_dl": "tb",
                    "rsv_sug3": "2",
                    "rsv_sug1": "2",
                    "rsv_sug7": "000",
                    "rsv_btype": "t",
                    "prefixsug": "httprunner",
                    "rsp": "1",
                    "inputT": "6648",
                    "rsv_sug4": "7252",
                    "rsv_sug": "2",
                    "bs": "httprunner 3",
                    "rsv_sid": "undefined",
                    "_ss": "1",
                    "clist": "",
                    "hsug": "httprunner 3\thttprunner",
                    "f4s": "1",
                    "csor": "10",
                    "_cr1": "40730",
                }
            )
            .with_headers(
                **{
                    "Host": "www.baidu.com",
                    "Connection": "keep-alive",
                    "Accept": "*/*",
                    "is_xhr": "1",
                    "X-Requested-With": "XMLHttpRequest",
                    "is_referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner%203&fenlei=256&oq=httprunner%203&rsv_pq=86a39119000039fe&rsv_t=2b6c1PBdGIcDYzEKyW9BkMzeCPMYcfbqTSf%2FEDXZuefGUrmcy2q1pxhJ0NQ&rqlang=cn",
                    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
                    "Sec-Fetch-Site": "same-origin",
                    "Sec-Fetch-Mode": "cors",
                    "Sec-Fetch-Dest": "empty",
                    "Referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner&fenlei=256&oq=httprunner%2520%2526lt%253B&rsv_pq=86a39119000039fe&rsv_t=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_sug3=2&rsv_sug1=2&rsv_sug7=000&rsv_btype=t&prefixsug=httprunner&rsp=1&inputT=6648&rsv_sug4=7252&rsv_sug=2",
                    "Accept-Encoding": "gzip, deflate, br",
                    "Accept-Language": "zh-CN,zh;q=0.9",
                    "Cookie": "BIDUPSID=EA49B0E234E0F93BBD3C0082A586CDEA; PSTM=1619952293; BAIDUID=C9FF25B24E5A3C59C96D61DB506725AB:FG=1; BD_UPN=123253; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=33986_33819_33849_33759_33675_33607_26350_33996; H_PS_645EC=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus; delPer=0; BD_CK_SAM=1; PSINO=5; BDSVRTM=14; WWW_ST=1620121549937",
                }
            )
            .with_cookies(
                **{
                    "BIDUPSID": "EA49B0E234E0F93BBD3C0082A586CDEA",
                    "PSTM": "1619952293",
                    "BAIDUID": "C9FF25B24E5A3C59C96D61DB506725AB:FG=1",
                    "BD_UPN": "123253",
                    "BDORZ": "B490B5EBF6F3CD402E515D22BCDA1598",
                    "H_PS_PSSID": "33986_33819_33849_33759_33675_33607_26350_33996",
                    "H_PS_645EC": "9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus",
                    "delPer": "0",
                    "BD_CK_SAM": "1",
                    "PSINO": "5",
                    "BDSVRTM": "14",
                    "WWW_ST": "1620121549937",
                }
            )
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal(‘headers."Content-Type"‘, "text/html;charset=utf-8")
        ),
    ]


if __name__ == "__main__":
    TestCaseBaiduHome().test_start()

因为httprunner封装了pytest,所有既可以用hrun去运行,也可以用pytest去运行。
hrun
HttpRunner V3.x 从入门到精通
pytest
HttpRunner V3.x 从入门到精通

8.3、转换为YAML/JSON

很简单,只要在命令后面多加对应的参数就行了。-2y/--to-ymlor-2j/--to-json
转为YAML:

har2case baidu_home.har -2y

HttpRunner V3.x 从入门到精通
可以查看到转换生成的yaml文件了。

config:
    name: testcase description
    variables: {}
    verify: false
teststeps:
-   name: /s
    request:
        cookies:
            BAIDUID: C9FF25B24E5A3C59C96D61DB506725AB:FG=1
            BDORZ: B490B5EBF6F3CD402E515D22BCDA1598
            BDSVRTM: ‘14‘
            BD_CK_SAM: ‘1‘
            BD_UPN: ‘123253‘
            BIDUPSID: EA49B0E234E0F93BBD3C0082A586CDEA
            H_PS_645EC: 9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus
            H_PS_PSSID: ‘33986_33819_33849_33759_33675_33607_26350_33996‘
            PSINO: ‘5‘
            PSTM: ‘1619952293‘
            WWW_ST: ‘1620121549937‘
            delPer: ‘0‘
        headers:
            Accept: ‘*/*‘
            Accept-Encoding: gzip, deflate, br
            Accept-Language: zh-CN,zh;q=0.9
            Connection: keep-alive
            Cookie: BIDUPSID=EA49B0E234E0F93BBD3C0082A586CDEA; PSTM=1619952293; BAIDUID=C9FF25B24E5A3C59C96D61DB506725AB:FG=1;
                BD_UPN=123253; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=33986_33819_33849_33759_33675_33607_26350_33996;
                H_PS_645EC=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus;
                delPer=0; BD_CK_SAM=1; PSINO=5; BDSVRTM=14; WWW_ST=1620121549937
            Host: www.baidu.com
            Referer: https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner&fenlei=256&oq=httprunner%2520%2526lt%253B&rsv_pq=86a39119000039fe&rsv_t=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_sug3=2&rsv_sug1=2&rsv_sug7=000&rsv_btype=t&prefixsug=httprunner&rsp=1&inputT=6648&rsv_sug4=7252&rsv_sug=2
            Sec-Fetch-Dest: empty
            Sec-Fetch-Mode: cors
            Sec-Fetch-Site: same-origin
            User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36
                (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
            X-Requested-With: XMLHttpRequest
            is_referer: https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner%203&fenlei=256&oq=httprunner%203&rsv_pq=86a39119000039fe&rsv_t=2b6c1PBdGIcDYzEKyW9BkMzeCPMYcfbqTSf%2FEDXZuefGUrmcy2q1pxhJ0NQ&rqlang=cn
            is_xhr: ‘1‘
        method: GET
        params:
            _cr1: ‘40730‘
            _ss: ‘1‘
            bs: httprunner 3
            clist: ‘‘
            csor: ‘10‘
            f: ‘8‘
            f4s: ‘1‘
            fenlei: ‘256‘
            hsug: "httprunner 3\thttprunner"
            ie: utf-8
            inputT: ‘6648‘
            isbd: ‘1‘
            isid: C9FF25725AB54698
            mod: ‘1‘
            oq: httprunner%20%26lt%3B
            prefixsug: httprunner
            rqlang: cn
            rsp: ‘1‘
            rsv_bp: ‘1‘
            rsv_btype: t
            rsv_dl: tb
            rsv_enter: ‘0‘
            rsv_idx: ‘1‘
            rsv_pq: 86a39119000039fe
            rsv_sid: undefined
            rsv_sug: ‘2‘
            rsv_sug1: ‘2‘
            rsv_sug3: ‘2‘
            rsv_sug4: ‘7252‘
            rsv_sug7: ‘000‘
            rsv_t: 9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ++oE9TlK6y2o+O7A7XdDS6Yus
            tn: baidu
            wd: httprunner
        url: https://www.baidu.com/s
    validate:
    -   eq:
        - status_code
        - 200
    -   eq:
        - headers.Content-Type
        - text/html;charset=utf-8

转换为JSON:

har2case baidu_home.har -2j

HttpRunner V3.x 从入门到精通
可以看的对应的json文件:

{
    "config": {
        "name": "testcase description",
        "variables": {},
        "verify": false
    },
    "teststeps": [
        {
            "name": "/s",
            "request": {
                "url": "https://www.baidu.com/s",
                "params": {
                    "ie": "utf-8",
                    "mod": "1",
                    "isbd": "1",
                    "isid": "C9FF25725AB54698",
                    "f": "8",
                    "rsv_bp": "1",
                    "rsv_idx": "1",
                    "tn": "baidu",
                    "wd": "httprunner",
                    "fenlei": "256",
                    "oq": "httprunner%20%26lt%3B",
                    "rsv_pq": "86a39119000039fe",
                    "rsv_t": "9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ++oE9TlK6y2o+O7A7XdDS6Yus",
                    "rqlang": "cn",
                    "rsv_enter": "0",
                    "rsv_dl": "tb",
                    "rsv_sug3": "2",
                    "rsv_sug1": "2",
                    "rsv_sug7": "000",
                    "rsv_btype": "t",
                    "prefixsug": "httprunner",
                    "rsp": "1",
                    "inputT": "6648",
                    "rsv_sug4": "7252",
                    "rsv_sug": "2",
                    "bs": "httprunner 3",
                    "rsv_sid": "undefined",
                    "_ss": "1",
                    "clist": "",
                    "hsug": "httprunner 3\thttprunner",
                    "f4s": "1",
                    "csor": "10",
                    "_cr1": "40730"
                },
                "method": "GET",
                "cookies": {
                    "BIDUPSID": "EA49B0E234E0F93BBD3C0082A586CDEA",
                    "PSTM": "1619952293",
                    "BAIDUID": "C9FF25B24E5A3C59C96D61DB506725AB:FG=1",
                    "BD_UPN": "123253",
                    "BDORZ": "B490B5EBF6F3CD402E515D22BCDA1598",
                    "H_PS_PSSID": "33986_33819_33849_33759_33675_33607_26350_33996",
                    "H_PS_645EC": "9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus",
                    "delPer": "0",
                    "BD_CK_SAM": "1",
                    "PSINO": "5",
                    "BDSVRTM": "14",
                    "WWW_ST": "1620121549937"
                },
                "headers": {
                    "Host": "www.baidu.com",
                    "Connection": "keep-alive",
                    "Accept": "*/*",
                    "is_xhr": "1",
                    "X-Requested-With": "XMLHttpRequest",
                    "is_referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner%203&fenlei=256&oq=httprunner%203&rsv_pq=86a39119000039fe&rsv_t=2b6c1PBdGIcDYzEKyW9BkMzeCPMYcfbqTSf%2FEDXZuefGUrmcy2q1pxhJ0NQ&rqlang=cn",
                    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
                    "Sec-Fetch-Site": "same-origin",
                    "Sec-Fetch-Mode": "cors",
                    "Sec-Fetch-Dest": "empty",
                    "Referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner&fenlei=256&oq=httprunner%2520%2526lt%253B&rsv_pq=86a39119000039fe&rsv_t=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_sug3=2&rsv_sug1=2&rsv_sug7=000&rsv_btype=t&prefixsug=httprunner&rsp=1&inputT=6648&rsv_sug4=7252&rsv_sug=2",
                    "Accept-Encoding": "gzip, deflate, br",
                    "Accept-Language": "zh-CN,zh;q=0.9",
                    "Cookie": "BIDUPSID=EA49B0E234E0F93BBD3C0082A586CDEA; PSTM=1619952293; BAIDUID=C9FF25B24E5A3C59C96D61DB506725AB:FG=1; BD_UPN=123253; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=33986_33819_33849_33759_33675_33607_26350_33996; H_PS_645EC=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus; delPer=0; BD_CK_SAM=1; PSINO=5; BDSVRTM=14; WWW_ST=1620121549937"
                }
            },
            "validate": [
                {
                    "eq": [
                        "status_code",
                        200
                    ]
                },
                {
                    "eq": [
                        "headers.Content-Type",
                        "text/html;charset=utf-8"
                    ]
                }
            ]
        }
    ]
}

以上转换出的pytest、yaml、json这3种格式的文件效果都是一样的,用hrun都可以运行,但是用pytest执行的话只可以运行.py的文件了。

九、 测试用例-结构解析

9.1、官方首推pytest格式

上篇文章我们知道了,httprunner可以支持三种格式的用例,分别是pytest、yaml和json。yaml和json是以前的版本所使用的用例格式,但是在3.x版本上,官方强烈建议使用的是pytest格式的用例。
HttpRunner V3.x 从入门到精通
上图是来自官方的用例格式关系图,可以看出来,httprunner再对于第三方导出的har文件进行了转换处理,有的人喜欢转换成json,有的人喜欢转换成yaml。但是最终,还是通过解析json格式的文件,生成pytest的python文件。
既然最后都是要生成pytest,那何不一步到位呢?哈哈,我想这就是官方推荐pytest格式的原因吧。
我还是挺喜欢的,因为我对于pytest使用的较多,那么接下来也是基于pytest格式的用例进行解析。

9.2、用例结构解析

录制生成的case很便捷,但是这并不是说,不需要我们做任何的改动了。在实践的过程中,我们仍然会根据我们实际项目的不同需求来对case作进一步的调整,所以彻底的了解case的构造尤为重要。
首先,我录制了一个百度搜索“httprunner”的一个请求,转换成pytest文件后如下:
HttpRunner V3.x 从入门到精通

可以看到:

  • 每个testcase都是HttpRunner的子类
  • 必须有两个类属性:config和teststeps。
  • 单个teststeps列表中的单个Step内部通过链式调用(RunRequest().get().with_params().with_header().with_cookies().validate().assert_equal())
  • config:配置测试用例级设置,包括基础url、验证、变量、导出。
  • teststeps:teststep的列表(list[Step]),每个步骤对应于一个API请求,也可以调用另一个testcase。此外,还支持variables/extract/validate/hooks机制来创建极其复杂的测试场景。
  • 链调用:可以看到一个case的请求,经过了各个环节的调用,这也是httprunner 3.x版本一大亮点。现在的ide编辑器越来越强大,比如你使用pycharm的话,都不用你怎么记忆用例的格式,顺手就...(点)出来了,这或许也是官方推荐使用pytest的另一个原因吧,哈哈。
    HttpRunner V3.x 从入门到精通
    HttpRunner V3.x 从入门到精通

9.3 httprunner的用例结构与常见的用例的比较

补一个官方完整的一个demo代码,并说说httprunner中的用例与我自己编写的测试用例之间的联系。

from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase


class TestCaseRequestWithFunctions(HttpRunner):
    config = (
        Config("request methods testcase with functions")
        .variables(
            **{
                "foo1": "config_bar1",
                "foo2": "config_bar2",
                "expect_foo1": "config_bar1",
                "expect_foo2": "config_bar2",
            }
        )
        .base_url("http://demo.qa.com")
        .verify(False)
        .export(*["foo3"])
    )

    teststeps = [
        Step(
            RunRequest("get with params")
            .with_variables(
                **{"foo1": "bar11", "foo2": "bar21", "sum_v": "${sum_two(1, 2)}"}
            )
            .get("/get")
            .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
            .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"})
            .extract()
            .with_jmespath("body.args.foo2", "foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.args.foo1", "bar11")
            .assert_equal("body.args.sum_v", "3")
            .assert_equal("body.args.foo2", "bar21")
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo2": "bar23"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "$expect_foo1")
            .assert_equal("body.form.foo2", "bar23")
            .assert_equal("body.form.foo3", "bar21")
        ),
    ]


if __name__ == "__main__":
    TestCaseRequestWithFunctions().test_start()
  1. httprunner中的testcase,其实说的就是上面的这一整个Python文件。
  2. teststeps列表中的Step,其实就是我自己编写case时候的一个个def test_xxx():pass。
  3. 每一个Step内部,依然是按照 传参——调用接口——断言,这样的过程来的。
    万变不离其宗,httprunner框架目前看起来,确实可以让编写更加的便捷、简洁,但是这只是目前从demo的过程中得到的结论,后面还需要落地实战才可以。

9.4、 测试用例-config

我们再一起来看,上面官方给出的例子

一、name(必填)

即用例名称,这是一个必填参数。测试用例名称,将显示在执行日志和测试报告中。比如,我在之前的百度搜索的case里,加入name。
HttpRunner V3.x 从入门到精通
运行后,在debug日志里,可以看的用例名称被展示出来。
HttpRunner V3.x 从入门到精通

二、base_url(选填)

其实这个配置一般在多环境切换中最常用。
比如你的这套测试用例在qa环境,uat环境都要使用,那么就可以把基础地址(举例http://demo.qa.com),设置进去。在后面的teststep中,只需要填上接口的相对路径就好了(举例 /get)。
这样的话,切换环境运行,只需要修改base_url即可。
HttpRunner V3.x 从入门到精通

三、variables(选填)

变量,这里可以存放一些公共的变量,可以在测试用例里引用。这里大家可以记住这个“公共”的词眼,因为在后面的Step中,还会有步骤变量。
比如说,我的接口有个传参是不变的,比如用户名username,而且后面的每个Step都会用到这个传参,那么username就可以放在config的公共变量里。
另外,Step里的变量优先级是比config里的变量要高的,如果有2个同名的变量的话,那么引用的时候,是优先引用步骤里的变量的。
HttpRunner V3.x 从入门到精通

四、verify(选填)

用来决定是否验证服务器TLS证书的开关。
通常设置为False,当请求https请求时,就会跳过验证。如果你运行时候发现抛错SSLError,可以检查一下是不是verify没传,或者设置了True。
HttpRunner V3.x 从入门到精通

五、export(选填)

导出的变量,主要是用于Step之间参数的传递。还是以上面的官方代码为例:
HttpRunner V3.x 从入门到精通
1.在config中配置export“foo3”这个变量。
2.在第一个Step中,.extract() 提取了"body.args.foo2"给变量“foo3”。
3.在第二个Step中,引用变量"foo3"。
像参数传递,提取这些点,会放在后面单独讲解,前面还是以熟悉框架为主。

9.5 、测试用例-teststeps-RunRequest

一、测试用例分层模型

一个testcase里(就是一个pytest格式的Python文件)可以有一个或者多个测试步骤,就是teststeps[]列表里的Step。

我的理解每一个Step就可以类比成pytest框架下的def test_xxx()的用例函数,在Step里通常都会要请求API完成测试,也可以调用其他测试用例来完成更多的需求。

可以来看下官方的测试用例逻辑图(2.x版本不同,3.x弃用了2.x的API概念):
HttpRunner V3.x 从入门到精通
可以看到,testsuite包含了testcase,testcase1需要依赖testcase2才可以完成,那么就可以在teststep12对其进行引用;而testcase2又依赖于testcase3,那么也可以在teststep22进行引用。
但是在整个testsuite下,这3个testcase都是相互独立的,可以独自运行。如果需要相互调用,则是在testcase内部去完成处理。

可能看起来有点绕,其实官方想表达的就是测试用例分层的一个思想:

测试用例(testcase)应该是完整且独立的,每条测试用例应该是都可以独立运行的
测试用例是测试步骤(teststep)的有序集合
测试用例集(testsuite)是测试用例的无序集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的;如果确实存在先后依赖关系,那就需要在测试用例中完成依赖的处理
其实这一点,在我们自己使用pytest框架编写测试用例的时候同样贯彻到了。为了自动化测试的稳定性和可维护性,每个测试用例之间相互独立是非常有必要的。

二、teststeps-RunRequest

先上一段Step的代码,结合下面的点对照着看:

    teststeps = [
        Step(
            RunRequest("get with params")
            .with_variables(
                **{"foo1": "bar11", "foo2": "bar21", "sum_v": "${sum_two(1, 2)}"}
            )
            .get("/get")
            .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
            .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"})
            .extract()
            .with_jmespath("body.args.foo2", "foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.args.foo1", "bar11")
            .assert_equal("body.args.sum_v", "3")
            .assert_equal("body.args.foo2", "bar21")
        ),

从上面的代码可以看出,RunRequest的作用就是在测试步骤中请求API,并且可以对于API的响应结果进行提取、断言。
1.RunRequest(name)
RunRequest的参数名用于指定teststep名称,它将显示在执行日志和测试报告中。
HttpRunner V3.x 从入门到精通
2. .with_variables
用于存放变量,但是这里的是Step步骤里的变量,不同的Step的变量是相互独立的。所以对于多个Step都要使用的变量,我们可以放到config的变量里去。

另外,如果config和Step里有重名的变量,那么当你引用这个变量的时候,Step变量会覆盖config变量。
HttpRunner V3.x 从入门到精通
3. .method(url)
这里就是指定请求API的方法了,常用的get、post等等。如图所示,就是用的get方法,括号里的url就是要请求的地址了。

这里要注意的是,如果在config里设置了基础url,那么步骤里的url就只能设置相对路径了。
HttpRunner V3.x 从入门到精通
4. .with_params
这个就简单了,测接口不都得要传参么,对于params类型的传参,就放这就行了,key-value键值对的形式。对于body形式的传参,看后面。
HttpRunner V3.x 从入门到精通
5. .with_headers
同样,有header要带上的就放这里。
HttpRunner V3.x 从入门到精通
6. .with_cookies
需要带cookie的,可以用.with_cookies方法。
HttpRunner V3.x 从入门到精通
7. .with_data
对于body类型的传参,可以用.with_data。
HttpRunner V3.x 从入门到精通
8. .with_json
如果是json类型的body请求体,可以用.with_json。
HttpRunner V3.x 从入门到精通
9. .extract
这里就是要做提取操作了,使用.with_jmespath(jmes_path: Text, var_name: Text)。

这里是采用了JMESPath语言,JMESPath是JSON的查询语言,可以便捷的提取json中你需要的元素。

第一个参数是你的目标元素的jmespath表达式,第二个元素则是用来存放这个元素的变量,供有需要的引用。

这里不展开,后面单讲。
HttpRunner V3.x 从入门到精通
10. .validate
断言,我们测试最终就是要验证接口返回是否符合预期。

那在httprunner框架中,可以使用assert_XXX(jmes_path: Text, expected_value: Any)来进行提取和验证。

第一个参数还是jmespath表达式,第二个参数则是预期值。

assert_XXX这种方式相信用过自动化测试框架的都不会陌生,所以也非常容易上手。目前httprunner还是封装了非常丰富的断言方法的,相信可以满足绝大多数的需求了。
HttpRunner V3.x 从入门到精通

9.6、测试用例-teststeps-RunTestCase

以前我在写接口自动化用例的时候,为了保证用例的独立性,需要在setUp里调用各种满足用例的一些前置条件,其中就不乏调用了其他测试用例中的方法。

而httprunner也是支持了这一项很重要的特性,通过RunTestCase对其他测试用例进行调用,并且还可以导出用例中你所需要的变量,来满足后续用例的的运行。

首先还是来看下RunTestCase的用法,然后再用实例去实践。下面是官方的一个演示代码。

import os
import sys

sys.path.insert(0, os.getcwd())

from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase

from examples.postman_echo.request_methods.request_with_functions_test import (
    TestCaseRequestWithFunctions as RequestWithFunctions,
)


class TestCaseRequestWithTestcaseReference(HttpRunner):
    config = (
        Config("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 = [
        Step(
            RunTestCase("request with functions")
            .with_variables(
                **{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}
            )
            .call(RequestWithFunctions)
            .export(*["foo3"])
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo1": "bar1"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "bar1")
            .assert_equal("body.form.foo2", "bar21")
        ),
    ]


if __name__ == "__main__":
    TestCaseRequestWithTestcaseReference().test_start()
  1. RunTestCase(name)
    这个参数呢还是一个名称,毕竟RunTestCase还是一个Step,这个名称同样会在日志和报告中显示。
    HttpRunner V3.x 从入门到精通
  2. .with_variables
    这个变量跟RunRequest里的用法一样。
  3. .call
    这里就是指定你要引用的testcase类名称了。
    HttpRunner V3.x 从入门到精通

这里需要特别说明下,导入的testcase需要起个别名,不能已test开头,会被系统识别成一个testcase

  1. .export
    可以指定要导出的变量,以供后续Step引用。
    可以看的.export()内部是一个列表[],这里可以用来导出多个变量。
    HttpRunner V3.x 从入门到精通

9.7、用例引用、变量传递

看到这里,对于httprunner已经有了一个大概的了解,现在想对于一些比较重要或者常用的功能,进行一些实践操作。
毕竟那谁说过,“纸上得来终觉浅,绝知此事要躬行。”
上一篇提到了RunTestCase,里面有2个重要的特征:

一个是在一个用例中引用另一个测试用例,另一个则是变量的导出与引用。

那就先来实践一下这2个货。
我用flask快速写了2个接口,以供在本地调用:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author:chenshifeng
@test_flask_demo.py
@time:2021/03/12
"""
from flask import Flask
from flask import request

app = Flask(__name__)


@app.route(‘/‘)
def hello_world():
    return ‘Hello World!‘


@app.route(‘/getUserName‘, methods=[‘GET‘])
def get_user_name():
    if request.method == ‘GET‘:
        return {
            "username": "chenshifeng",
            "age": "18",
            "from": "China",
        }


@app.route(‘/joinStr‘, methods=[‘GET‘])
def str_join():
    if request.method == ‘GET‘:
        str1 = request.args.get("str1")
        str2 = request.args.get("str2")
        after_join = str1 + " " + str2
        return {
            "result": after_join
        }


if __name__ == ‘__main__‘:
    app.run()

一共有2个接口:

  1. /getUserName,查询用户名,返回是我写死的字典。
  2. /joinStr,两个字符串拼接,返回的是拼接后的结果。
    一、编写测试用例
    根据之前学习过的,直接编写case,因为这个接口没有传参,cookie之类的,就省掉了,只是demo用。
  3. 接口:/getUserName
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author:chenshifeng
@file:test_getUserName_demo.py
@time:2021/05/05
"""
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase


class TestCaseRequestWithGetUserName(HttpRunner):
    config = (
        Config("test /getUserName")
        .base_url("http://localhost:5000")
        .verify(False)
    )

    teststeps = [
        Step(
            RunRequest("getUserName")
            .get("/getUserName")
            .validate()
            .assert_equal("body.username", "chenshifeng")
        ),

    ]


if __name__ == "__main__":
    TestCaseRequestWithGetUserName().test_start()

这里呢,步骤都有了,断言是验证返回的username字段值是不是“chenshifeng”,运行一下,可以看到测试通过。
HttpRunner V3.x 从入门到精通
2. 接口:/joinStr
这个接口就需要2个传参了,那么在Step里通过.with_params()来传参。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author:chenshifeng
@file:joinStr_demo_test.py
@time:2021/05/05
"""
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase


class TestCaseRequestWithJoinStr(HttpRunner):
    config = (
        Config("test /joinStr")
        .base_url("http://localhost:5000")
        .verify(False)
    )

    teststeps = [
        Step(
            RunRequest("joinStr")
            .get("/joinStr")
            .with_params(**{"str1": "hello", "str2": "chenshifeng"})
            .validate()
            .assert_equal("body.result", "hello chenshifeng")
        ),

    ]


if __name__ == "__main__":
    TestCaseRequestWithJoinStr().test_start()

这里传入的参数分别是“hello”、“chenshifeng”,这个字符串在拼接的时候是加了一个空格的,所以断言的时候我预期的值是"hello chenshifeng"。
运行测试,可以看到测试通过。
HttpRunner V3.x 从入门到精通
二、testcase引用&变量传递
以上是2个分开的case,都可以分别正常运行。

假设,/joinStr接口的第二个参数,是依赖/getUserName接口的返回,那么现在这2个testcase之间就有了依赖关系。

那么在写/getUserName接口用例的时候,就需要去引用/joinStr的测试用例了,并且需要把/getUserName用例的变量导出来,/joinStr的测试用例传参时候使用。

  1. 首先,先修改/getUserName接口的case:
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase


class TestCaseRequestWithGetUserName(HttpRunner):
    config = (
        Config("test /getUserName")
        .base_url("http://localhost:5000")
        .verify(False)
        .export(*["username"])  # 这里定义出要导出的变量
    )

    teststeps = [
        Step(
            RunRequest("getUserName")
            .get("/getUserName")
            .extract()
            .with_jmespath("body.username", "username")  # 提取出目标值,赋值给username变量
            .validate()
            .assert_equal("body.username", "chenshifeng")
        ),

    ]


if __name__ == "__main__":
    TestCaseRequestWithGetUserName().test_start()

关注注释部分的代码,一个是config里定义了这个要导出的变量,另一个是在Step中,讲目标值提取出来,赋值给这个变量。
2. 接下来,修改/joinStr接口的测试用例:

from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from testcases.test_getUserName_demo import TestCaseRequestWithGetUserName as RequestWithGetUserName   # 记得要导入引用的类


class TestCaseRequestWithJoinStr(HttpRunner):
    config = (
        Config("test /joinStr")
        .base_url("http://localhost:5000")
        .verify(False)
    )

    teststeps = [
        Step(
            RunTestCase("setUp getUserName")
            .call(RequestWithGetUserName)   # 导入后就可以调用了
            .export(*["username"])  # 在RunTestCase步骤中定义这个变量的导出
        ),
        Step(
            RunRequest("joinStr")
            .get("/joinStr")
            .with_params(**{"str1": "hello", "str2": "$username"})  # 在第二个传参中引用导出的变量
            .validate()
            .assert_equal("body.result", "hello $username")   # 断言的预期值也引用变量
        ),

    ]


if __name__ == "__main__":
    TestCaseRequestWithJoinStr().test_start()

按照直接学习的内容,case已经修改好,现在运行/joinStr接口的测试用例,可以看到运行通过。
HttpRunner V3.x 从入门到精通
刚接触httprunner的这个pytest格式的语法时候,感觉还不习惯,但是你跟着实践走下来,发现习惯的还是很快的,快动手试试吧。

十、总结:运行testcase的几种方式

在之前的demo过程中,已经运行过testcase了,那这篇就也来汇总一下,运行case相关的知识点。

10.1、运行testcase的几种场景

  1. 运行单个case
    通常单个case的话我会在编辑器里用main方法运行,不过也可以用命令行运行,看你喜欢。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author:chenshifeng
@file:joinStr_demo_test.py
@time:2021/05/05
"""
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from testcases.test_getUserName_demo import TestCaseRequestWithGetUserName as RequestWithGetUserName   # 记得要导入引用的类


class TestCaseRequestWithJoinStr(HttpRunner):
    config = (
        Config("test /joinStr")
        .base_url("http://localhost:5000")
        .verify(False)
    )

    teststeps = [
        Step(
            RunTestCase("setUp getUserName")
            .call(RequestWithGetUserName)   # 导入后就可以调用了
            .export(*["username"])  # 在RunTestCase步骤中定义这个变量的导出
        ),
        Step(
            RunRequest("joinStr")
            .get("/joinStr")
            .with_params(**{"str1": "hello", "str2": "$username"})  # 在第二个传参中引用导出的变量
            .validate()
            .assert_equal("body.result", "hello $username")   # 断言的预期值也引用变量
        ),

    ]


if __name__ == "__main__":
    TestCaseRequestWithJoinStr().test_start()   # 这里

main方法里在类的后面调用test_start()方法即可。
命令行的话,就是直接在hrun后面加上case的路径,就可以运行了。

chenshifengdeMacBook-Pro:httprunner_demo chenshifeng$ hrun testcases/demo_testcase_request_test.py 
2021-05-05 01:04:33.386 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request_test.py
2021-05-05 01:04:33.387 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
No Path provided. Nothing to do ??
2021-05-05 01:04:33.581 | INFO     | httprunner.cli:main_run:56 - start to run tests with pytest. HttpRunner version: 3.1.4
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.9.2, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo
plugins: metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 1 item                                                                                                                                                              

testcases/demo_testcase_request_test.py .                                                                                                                               [100%]

============================================================================== 1 passed in 2.28s ==============================================================================
chenshifengdeMacBook-Pro:httprunner_demo chenshifeng$ hrun testcases/getUserName_demo_test.py 
2021-05-05 01:05:13.318 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/getUserName_demo_test.py
2021-05-05 01:05:13.318 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
No Path provided. Nothing to do ??
2021-05-05 01:05:13.510 | INFO     | httprunner.cli:main_run:56 - start to run tests with pytest. HttpRunner version: 3.1.4
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.9.2, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo
plugins: metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 1 item                                                                                                                                                              

testcases/getUserName_demo_test.py .                                                                                                                                    [100%]

============================================================================== 1 passed in 0.18s ==============================================================================
Sentry is attempting to send 0 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit
  1. 运行多个case
    也可以选择运行多个case,hrun后面多个路径之间用空格隔开。
chenshifengdeMacBook-Pro:httprunner_demo chenshifeng$ hrun testcases/getUserName_demo_test.py  testcases/joinStr_demo_test.py 
2021-05-05 01:12:04.043 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/getUserName_demo_test.py
2021-05-05 01:12:04.044 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/joinStr_demo_test.py
2021-05-05 01:12:04.044 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
No Path provided. Nothing to do ??
2021-05-05 01:12:04.231 | INFO     | httprunner.cli:main_run:56 - start to run tests with pytest. HttpRunner version: 3.1.4
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.9.2, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo
plugins: metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 2 items                                                                                                                                                             

testcases/getUserName_demo_test.py .                                                                                                                                    [ 50%]
testcases/joinStr_demo_test.py .                                                                                                                                        [100%]

============================================================================== 2 passed in 0.22s ==============================================================================
Sentry is attempting to send 0 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit
  1. 运行整个case文件夹
    通常在testcases这个目录下会存放我们的测试用例,那么也可以直接运行这个目录的路径,来运行下面所有的case。
chenshifengdeMacBook-Pro:httprunner_demo chenshifeng$ hrun testcases/
2021-05-05 01:13:05.177 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/
2021-05-05 01:13:05.183 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-05 01:13:05.186 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/.env
2021-05-05 01:13:05.187 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2021-05-05 01:13:05.187 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2021-05-05 01:13:05.187 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_ref.yml
2021-05-05 01:13:05.199 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-05 01:13:05.200 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request.yml
2021-05-05 01:13:05.200 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request_test.py
2021-05-05 01:13:05.201 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_ref_test.py
2021-05-05 01:13:05.211 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-05 01:13:05.213 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request.yml
2021-05-05 01:13:05.213 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_ref_test.py
reformatted /Users/chenshifeng/MyCode/PythonCode/httprunner_demo/testcases/demo_testcase_request_test.py
All done! ? ?? ?
2 files reformatted.
2021-05-05 01:13:05.687 | INFO     | httprunner.cli:main_run:56 - start to run tests with pytest. HttpRunner version: 3.1.4
============================================================================= test session starts =============================================================================
platform darwin -- Python 3.9.2, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode/httprunner_demo
plugins: metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 4 items                                                                                                                                                             

testcases/demo_testcase_ref_test.py .                                                                                                                                   [ 25%]
testcases/joinStr_demo_test.py .                                                                                                                                        [ 50%]
testcases/demo_testcase_request_test.py .                                                                                                                               [ 75%]
testcases/getUserName_demo_test.py .                                                                                                                                    [100%]

============================================================================== 4 passed in 3.93s ==============================================================================

10.2、运行YAML/JSON文件格式的case

如果运行YAML/JSON文件,同级文件夹下,就会生成对应的pytest的文件,文件名称的末尾会有_test。其实httprunner会先把它们转换为pytest格式的,再去运行。
所以,用httprunner 3.x版本的话,写case的话还是直接用pytest格式吧。

10.3、运行pytest格式的文件

对应pytest文件来说,用hrun或者pytest命令运行都是可以的。
因为hrun只是封装了pytest,所以pytest运行的所有参数,同样可以在hrun后面加。

HttpRunner V3.x 从入门到精通

上一篇:MVC之实现基于token的认证


下一篇:让vs .net2 3 3.5 内置库拥有文档注释