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打開,可以看的生成的目錄結構如下圖,那么這些都是什么意思呢?
image

  • debugtalk.py 放置在項目根目錄下(借鑒了pytest的conftest.py文件的設計)
  • .env 放置在項目根目錄下,可以用於存放一些環境變量
  • reports 文件夾:存儲 HTML 測試報告
  • testcases 用於存放測試用例
  • har 可以存放錄制導出的.har文件
    具體用法會在后續中細講,我們可以點開生成的testcases文件夾下的測試用例,里面是提供了一個可運行的demo內容的,那先來運行一下看看。
    image

運行用例:
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的請求。
image
選中這個請求,點擊左上角的File——Export Sessions——(可以選擇導出選中的也可以導出所有),這里我們選擇導出選中的,導出HTTPArchive,文件名baidu_home.har,保存到了項目的har目錄下。
image
image

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
image
pytest
image

8.3、轉換為YAML/JSON

很簡單,只要在命令后面多加對應的參數就行了。-2y/--to-yml 或者-2j/--to-json
轉為YAML:

har2case baidu_home.har -2y

image
可以查看到轉換生成的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

image
可以看的對應的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格式的用例。
image
上圖是來自官方的用例格式關系圖,可以看出來,httprunner再對於第三方導出的har文件進行了轉換處理,有的人喜歡轉換成json,有的人喜歡轉換成yaml。但是最終,還是通過解析json格式的文件,生成pytest的python文件。
既然最后都是要生成pytest,那何不一步到位呢?哈哈,我想這就是官方推薦pytest格式的原因吧。
我還是挺喜歡的,因為我對於pytest使用的較多,那么接下來也是基於pytest格式的用例進行解析。

9.2、用例結構解析

錄制生成的case很便捷,但是這並不是說,不需要我們做任何的改動了。在實踐的過程中,我們仍然會根據我們實際項目的不同需求來對case作進一步的調整,所以徹底的了解case的構造尤為重要。
首先,我錄制了一個百度搜索“httprunner”的一個請求,轉換成pytest文件后如下:
image

可以看到:

  • 每個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的另一個原因吧,哈哈。
    image
    image

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。
image
運行后,在debug日志里,可以看的用例名稱被展示出來。
image

二、base_url(選填)

其實這個配置一般在多環境切換中最常用。
比如你的這套測試用例在qa環境,uat環境都要使用,那么就可以把基礎地址(舉例http://demo.qa.com),設置進去。在后面的teststep中,只需要填上接口的相對路徑就好了(舉例 /get)。
這樣的話,切換環境運行,只需要修改base_url即可。
image

三、variables(選填)

變量,這里可以存放一些公共的變量,可以在測試用例里引用。這里大家可以記住這個“公共”的詞眼,因為在后面的Step中,還會有步驟變量。
比如說,我的接口有個傳參是不變的,比如用戶名username,而且后面的每個Step都會用到這個傳參,那么username就可以放在config的公共變量里。
另外,Step里的變量優先級是比config里的變量要高的,如果有2個同名的變量的話,那么引用的時候,是優先引用步驟里的變量的。
image

四、verify(選填)

用來決定是否驗證服務器TLS證書的開關。
通常設置為False,當請求https請求時,就會跳過驗證。如果你運行時候發現拋錯SSLError,可以檢查一下是不是verify沒傳,或者設置了True。
image

五、export(選填)

導出的變量,主要是用於Step之間參數的傳遞。還是以上面的官方代碼為例:
image
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概念):
image
可以看到,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名稱,它將顯示在執行日志和測試報告中。
image

2. .with_variables

用於存放變量,但是這里的是Step步驟里的變量,不同的Step的變量是相互獨立的。所以對於多個Step都要使用的變量,我們可以放到config的變量里去。

另外,如果config和Step里有重名的變量,那么當你引用這個變量的時候,Step變量會覆蓋config變量。
image

3. .method(url)

這里就是指定請求API的方法了,常用的get、post等等。如圖所示,就是用的get方法,括號里的url就是要請求的地址了。

這里要注意的是,如果在config里設置了基礎url,那么步驟里的url就只能設置相對路徑了。
image

4. .with_params

這個就簡單了,測接口不都得要傳參么,對於params類型的傳參,就放這就行了,key-value鍵值對的形式。對於body形式的傳參,看后面。
image

5. .with_headers

同樣,有header要帶上的就放這里。
image

6. .with_cookies

需要帶cookie的,可以用.with_cookies方法。
image

7. .with_data

對於body類型的傳參,可以用.with_data。
image

8. .with_json

如果是json類型的body請求體,可以用.with_json。
image

9. .extract

這里就是要做提取操作了,使用.with_jmespath(jmes_path: Text, var_name: Text)。

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

第一個參數是你的目標元素的jmespath表達式,第二個元素則是用來存放這個元素的變量,供有需要的引用。

這里不展開,后面單講。
image

10. .validate

斷言,我們測試最終就是要驗證接口返回是否符合預期。

那在httprunner框架中,可以使用assert_XXX(jmes_path: Text, expected_value: Any)來進行提取和驗證。

第一個參數還是jmespath表達式,第二個參數則是預期值。

assert_XXX這種方式相信用過自動化測試框架的都不會陌生,所以也非常容易上手。目前httprunner還是封裝了非常豐富的斷言方法的,相信可以滿足絕大多數的需求了。
image

11. .upload

主要用於文件上傳,以下提供一個案例:

 teststeps = [
        Step(
            RunRequest("/upc/oss/upload")
            .post("http://xxxxx/upc/oss/upload")
            .with_cookies(**{"ACCESS_TOKEN": "fa432c1dba0a443b9def0e66c6bd7736"})
            .upload(**{"file": "/Users/chenshifeng/Desktop/圖片/d000baa1cd11728bcdde8185ccfcc3cec2fd2ca1.jpg", "title": "titlename"})
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal('headers."Content-Type"', "application/json;charset=UTF-8")
            .assert_equal("body.success", True)
            .assert_equal("body.errCode", "0")
            .assert_equal("body.errMsg", "success")
        ),
    ]

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,這個名稱同樣會在日志和報告中顯示。
    image
  2. .with_variables
    這個變量跟RunRequest里的用法一樣。
  3. .call
    這里就是指定你要引用的testcase類名稱了。
    image

這里需要特別說明下,導入的testcase需要起個別名,不能已test開頭,會被系統識別成一個testcase

  1. .export
    可以指定要導出的變量,以供后續Step引用。
    可以看的.export()內部是一個列表[],這里可以用來導出多個變量。
    image

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用。

  1. 接口:/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”,運行一下,可以看到測試通過。
image
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"。
運行測試,可以看到測試通過。
image
二、testcase引用&變量傳遞
以上是2個分開的case,都可以分別正常運行。

假設,/joinStr接口的第二個參數,是依賴/getUserName接口的返回,那么現在這2個testcase之間就有了依賴關系。

那么在寫/joinStr接口用例的時候,就需要去引用/getUserName的測試用例了,並且需要把/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接口的測試用例,可以看到運行通過。
image
剛接觸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后面加。

十一、測試報告

11.1 HTML報告

Httprunner 安裝之后自帶 pytest-html插件,當你想生成 HTML測試報告時,可以添加命令參數--html

hrun testcases/demo_testcase_request.yml --html=reports/demo_report.html

--html=reports/demo_report.html中的reports/demo_report.html是測試報告的存放路徑,沒有帶文件夾的時候會存放在命令運行的當前文件夾,此處是項目根目錄

chenshifengdeMacBook-Pro:interface chenshifeng$  hrun testcases/demo_testcase_request.yml --html=reports/demo_report.html
2021-05-06 22:57:17.072 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request.yml
2021-05-06 22:57:17.083 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-06 22:57:17.087 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/.env
2021-05-06 22:57:17.088 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2021-05-06 22:57:17.088 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2021-05-06 22:57:17.089 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request.yml
2021-05-06 22:57:17.090 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request_test.py
2021-05-06 22:57:17.090 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request_test.py
All done! ✨ 🍰 ✨
1 file reformatted.
2021-05-06 22:57:17.375 | 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-5.4.3, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface
plugins: testconfig-0.2.0, metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 1 item                                                                                                                                                              

testcases/demo_testcase_request_test.py .                                                                                                                               [100%]

--------------------------------- generated html file: file:///Users/chenshifeng/MyCode/PythonCode/SFtest/interface/reports/demo_report.html ----------------------------------
============================================================================== 1 passed in 6.97s ==============================================================================

生成的報告如下
image

這個報告大家了解一下即可,接下來着重說說大名鼎鼎的 allure 測試報告。

10.2 allure報告

HTTPrunner 集成了 pytest,所以 HTTPrunner v3.x可以使用pytest 的所有插件,包括測試報告插件,例如pytest-html 和alluer-pytest。
不過 HTTPrunner 默認並未安裝 allure,你需要另外安裝。
安裝有兩種方式:

  • 安裝allure的 pytest 依賴庫allure-pytest;
  • 安裝 HTTPrunner的allure 依賴庫 httprunner[allure]。

安裝 allure-pytest:

pip3 install allure-pytest

安裝 httprunnerallure

pip3 install "httprunner[allure]"

一旦allure-pytest 准備好,以下參數就可以與 hrun/pytest命令一起使用:

  • --alluredir=DIR: 生成 allure 報告到指定目錄
  • --clean-alluredir: 如果指定目錄已存在則清理該文件夾
  • --allure-no-capture:不要將 pytest 捕獲的日志記錄(logging)、標准輸出(stdout)、標准錯誤(stderr)附加到報告中
    要使 Allure 偵聽器能夠在測試執行期間收集結果,只需添加--alluredir選項,並提供指向存儲結果的文件夾路徑。如:
 hrun testcases/ --alluredir=reports/allure_reports/ --clean-alluredir
 chenshifengdeMacBook-Pro:interface chenshifeng$ hrun testcases/ --alluredir=reports/allure_reports/ --clean-alluredir
2021-05-06 23:10:19.710 | INFO     | httprunner.make:__make:512 - make path: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/
2021-05-06 23:10:19.716 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-06 23:10:19.720 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/.env
2021-05-06 23:10:19.720 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2021-05-06 23:10:19.721 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2021-05-06 23:10:19.721 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_ref.yml
2021-05-06 23:10:19.732 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-06 23:10:19.733 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request.yml
2021-05-06 23:10:19.733 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request_test.py
2021-05-06 23:10:19.734 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_ref_test.py
2021-05-06 23:10:19.744 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2021-05-06 23:10:19.744 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request.yml
2021-05-06 23:10:19.745 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_ref_test.py
reformatted /Users/chenshifeng/MyCode/PythonCode/SFtest/interface/testcases/demo_testcase_request_test.py
All done! ✨ 🍰 ✨
2 files reformatted.
2021-05-06 23:10:20.218 | 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-5.4.3, py-1.10.0, pluggy-0.13.1
rootdir: /Users/chenshifeng/MyCode/PythonCode/SFtest/interface
plugins: testconfig-0.2.0, metadata-1.11.0, html-2.1.1, allure-pytest-2.8.40
collected 2 items                                                                                                                                                             

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

============================================================================== 2 passed in 5.17s ==============================================================================

reports/allure_reports/ 只會存儲收集的測試結果並非完成的報告,還需要通過命令生成。

要在測試完成后查看實際報告,您需要使用Allure命令行實用程序從結果中生成報告。

allure serve reports/allure_reports/
 
chenshifengdeMacBook-Pro:interface chenshifeng$ allure serve reports/allure_reports/
Generating report to temp directory...
Report successfully generated to /var/folders/p0/3_7fwrvx6n3ftpfd4wjb01300000gn/T/6934992980047989302/allure-report
Starting web server...
2021-05-06 23:15:35.507:INFO::main: Logging initialized @7514ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://192.168.12.105:63501/>. Press <Ctrl+C> to exit

此命令將在默認瀏覽器中顯示你生成的報告。

image
關於allure的更多使用方法參考:https://www.cnblogs.com/feng0815/p/13792188.html
同時你也可以在 Jenkins 中安裝 allure 報告插件,將結果與Jenkins 集成。

補充

2021.6.4進行如下補充:

1、輔助函數debugtalk.py

你是否會遇到這樣的問題,對於一些需要實時變化的參數或要求唯一性校驗的參數,在進行自動化時不知道怎么實時改變參數的值?
別怕,現在它來了,httprunner的輔助函數功能可以解決你的所有煩惱。
httprunner的輔助函數debugtalk.py在你生成腳手架時會自動生成在項目的根目錄下,默認只有3個函數
image
我們可以根據自己的實際需要來增加函數,那么該如何調用的,非常簡單,我們只需要在任何需要使用的地方通過${function()}方法調用即可,

舉例說明,比如我在做接口自動化的過程中有個填寫身份證的入參,且有唯一性校驗,還有賬號、姓名、手機號字段需要有唯一性校驗,那么我就寫的個隨機生成身份證、生成隨機數和隨機生成手機號的函數

def get_random_number(digit: int):
    '''
    生成隨機數
    Args:
        digit: 需要生成多少位的隨機數

    Returns:所需要的隨機數

    '''
    return ''.join([random.choice([str(i) for i in range(9)]) for i in range(digit)])

def create_ident_generator():
    '''隨機生成身份證'''

    # 身份證號的前兩位,省份代號
    sheng = (
        '11', '12', '13', '14', '15', '21', '22', '23', '31', '32', '33', '34', '35', '36', '37', '41', '42', '43',
        '44',
        '45', '46', '50', '51', '52', '53', '54', '61', '62', '63', '64', '65', '66')
    # 隨機選擇距離今天在7000到25000的日期作為出生日期(沒有特殊要求我就隨便設置的,有特殊要求的此處可以完善下)
    birthdate = (datetime.date.today() - datetime.timedelta(days=random.randint(7000, 25000)))
    # 拼接出身份證號的前17位(第3-第6位為市和區的代碼,中國太大此處就偷懶了寫了定值,有要求的可以做個隨機來完善下;第15-第17位為出生的順序碼,隨機在100到199中選擇)
    ident = sheng[random.randint(0, 31)] + '0101' + birthdate.strftime("%Y%m%d") + str(random.randint(100, 199))

    # 前17位每位需要乘上的系數,用字典表示,比如第一位需要乘上7,最后一位需要乘上2
    coe = {1: 7, 2: 9, 3: 10, 4: 5, 5: 8, 6: 4, 7: 2, 8: 1, 9: 6, 10: 3, 11: 7, 12: 9, 13: 10, 14: 5, 15: 8, 16: 4,
           17: 2}
    summation = 0

    # for循環計算前17位每位乘上系數之后的和
    for i in range(17):
        summation = summation + int(ident[i:i + 1]) * coe[i + 1]  # ident[i:i+1]使用的是python的切片獲得每位數字

    # 前17位每位乘上系數之后的和除以11得到的余數對照表,比如余數是0,那第18位就是1
    key = {0: '1', 1: '0', 2: 'X', 3: '9', 4: '8', 5: '7', 6: '6', 7: '5', 8: '4', 9: '3', 10: '2'}

    # 拼接得到完整的18位身份證號
    return ident + key[summation % 11]

def create_phone_generator():
    '''
    隨機生成手機號
    Returns:

    '''
    # 第二位數字
    second = [3, 4, 5, 7, 8][random.randint(0, 4)]

    # 第三位數字
    third = {
        3: random.randint(0, 9),
        4: [5, 7, 9][random.randint(0, 2)],
        5: [i for i in range(10) if i != 4][random.randint(0, 8)],
        7: [i for i in range(10) if i not in [4, 9]][random.randint(0, 7)],
        8: random.randint(0, 9),
    }[second]

    # 最后八位數字
    suffix = random.randint(9999999, 100000000)

    # 拼接手機號
    return "1{}{}{}".format(second, third, suffix)

然后再腳本中直接調用這幾個個函數即可
image
運行,查看結果
image
可以看出,每次都根據函數生成的不同的值。

2、全局變量.env

httprunner腳手架會默認創建創建.env文件,將需要設置為環境變量或全局變量的值,存儲在.env中
它與debugtalk.py文件一樣,存放在項目跟目錄下。
然后我們可以通過${ENV(VariableName)}調用即可
舉例說明,下圖是我的.env文件存放的常量
image
如上圖。我存放了項目的基本url和登陸cookie,其他cookie是每次運行登陸腳本自動寫進去的,然后在所需要的地方直接調用,當然,你也可以在所需要cookie的地方直接調佣登陸函數 ,看你喜歡那種方法了。
然后在所需要的地方調用
image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM