接口自動化之HttpRunner初探 |Python 主題月

作者:波小藝

介紹:

HttpRunner是一個簡單優(yōu)雅但功能強大的 HTTP(S) 測試框架缠俺。以YAML或JSON格式定義測試用例琉挖,保障測試用例描述的統(tǒng)一性和可維護性暂论。程序執(zhí)行的時候裂明,會處理用戶輸入的yml/json文件并基于模板生成測試文件六孵。最終通過pytest.main([])的方式去執(zhí)行生成的用例文件纬黎。用戶只需要通過json/yml文件去維護用例即可,不需要關(guān)心程序如何處理json/yml文件劫窒,如何生成測試文件等本今,簡單快速通過pytest運行用例,并獲取詳細(xì)的測試報告主巍。

主要特征:

  • 以YAML或JSON格式定義測試用例冠息,保障測試用例描述的統(tǒng)一性和可維護性
    • testsuite > testcase > teststep(api)
    • 支持設(shè)計一系列的測試場景,每個測試場景可包含多個teststep煤禽。
    • 支持參數(shù)化設(shè)計
    • 支持variables/ extract/ validate/hooks機制(使用jmespath铐达,提取和驗證json響應(yīng))
    • 支持添加邏輯運算輔助函數(shù)(debugtalk.py),在測試腳本中實現(xiàn)復(fù)雜的動態(tài)邏輯
  • 在 HAR 支持下記錄并生成測試用例檬果。(使用charles去抓取請求,生成的用例文件可能還需要手動處理)
  • 使用 pytest 執(zhí)行測試文件 ,數(shù)百個插件隨時可用选脊。使用 allure杭抠,測試報告可以非常強大。
    • run_testcase():處理請求前的數(shù)據(jù)
    • __run_step() > __run_step_request(使用requests發(fā)起api請求并導(dǎo)出其他用例引用的變量(__step_datas))
  • 通過重復(fù)使用locust 磺箕,您可以進(jìn)行性能測試啄骇,而無需進(jìn)行額外的工作坐桩。
  • 支持CLI命令,與CI/CD完美結(jié)合翁垂。

工作流程

想要了解其工作流程,最好的辦法就是使用debug模式硝桩,那在這里應(yīng)該如何使用debug來參透httprunner的執(zhí)行流程呢沿猜?跟著我來看下:

調(diào)試技巧

使用pycharm進(jìn)行調(diào)試,且python環(huán)境使用Virtualenv進(jìn)行管理

  • 安裝:pip install httprunner
  • /venv/bin/ 目錄下找到hrun文件
  • 編輯hrun文件碗脊,為sys.argv賦值啼肩。第一個是hrun的絕對路徑,第二個是用例的絕對路徑
  • sys.exit(main_hrun_alias())這里打個斷點衙伶,那么就可以開始愉快的調(diào)試之旅了~
# -*- coding: utf-8 -*-
import re
import sys
from httprunner.cli import main_hrun_alias
if __name__ == '__main__':
    sys.argv = ['/Users/boyizhang/PycharmProjects/apitest/venv/bin/hrun', '/Users/boyizhang/PycharmProjects/apitest/hruntests/testcases/testheader.yml']

    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main_hrun_alias())

工作流

調(diào)試日志:

  • 進(jìn)入main_hrun_alias()中祈坠,并為sys.argv的列表插入新的一項“run”(因為我們是執(zhí)行用例)
  • 進(jìn)入main(),因為我們含有“run”矢劲,所以現(xiàn)在開始執(zhí)行用例赦拘。
  • 進(jìn)入main_run(extra_args),根據(jù)extra_args(yml/json路徑)將yml/json文件生成python文件(測試文件)
  • 進(jìn)入main_make()芬沉,判斷路徑是否存在
  • 進(jìn)入 __make() 另绩,加載yml/json文件,并準(zhǔn)備數(shù)據(jù)(按照數(shù)據(jù)的優(yōu)先級進(jìn)行復(fù)制花嘶,從低到高)
  • 進(jìn)入make_testcase()笋籽,將yml/json文件基于模板生成對應(yīng)的py文件
  • 最后回到main_run()方法,繼續(xù)執(zhí)行main_make()以下的代碼椭员,可以發(fā)現(xiàn)车海,程序最終將創(chuàng)建的py文件交給pytest.main()來執(zhí)行
  • 上面演示了用例文件從yml/json到py文件的生成流程。接下來可以對生成的py文件繼續(xù)調(diào)試隘击。
  • 這里我做了一個比較簡單的演示侍芝,小伙伴們?nèi)绻信d趣,可以根據(jù)我的思路繼續(xù)你的調(diào)試之旅哦埋同。

用例管理

關(guān)鍵概念:

概括來說州叠,測試用例分層機制的核心是將接口定義、測試步驟凶赁、測試用例咧栗、測試場景進(jìn)行分離逆甜,單獨進(jìn)行描述和維護,從而盡可能地減少自動化測試用例的維護成本致板。

  • 測試用例(testcase)應(yīng)該是完整且獨立的交煞,每條測試用例應(yīng)該是都可以獨立運行
  • 測試用例(測試場景)是測試步驟(teststep)的 **有序 **集合,每一個測試步驟對應(yīng)一個 API 的請求描述
  • 測試用例集(testsuite)是測試用例的 無序 集合斟或,集合中的測試用例應(yīng)該都是相互獨立素征,不存在先后依賴關(guān)系的;如果確實存在先后依賴關(guān)系萝挤,那就需要在測試用例中完成依賴的處理
    • 多個測試場景組成一個測試套件御毅,運行一個測試套件,可以同時執(zhí)行多個測試場景怜珍。
  • 接口定義(api definition):為了更好地對接口描述進(jìn)行管理端蛆,使用獨立的文件對接口描述進(jìn)行存儲,即每個文件對應(yīng)一個接口描述绘面。

V2.0

建議大家用最新版本的欺税,但是有興趣了解httprunner的架構(gòu)變化的同學(xué),可以繼續(xù)讀V2.0模塊的內(nèi)容揭璃。

測試用例分層模型

V2.0的版本中晚凿,存在api definition的概念。

  • 所有的api都置于api文件夾瘦馍,每個api創(chuàng)建一個yml/json文件進(jìn)行管理
  • testcase中的teststep引用對應(yīng)的api

優(yōu)點是:api管理方便歼秽;缺點是:在testcase中的teststep可以直接引用api definition,不管testcase是單個步驟的簡單場景還是多個步驟的復(fù)雜場景情组,都需要進(jìn)行引用api definition燥筷。這樣的話,對于單個步驟的簡單場景而言院崇,又和api definition很相似肆氓,這樣就會造成重復(fù)描述,而且容易混淆底瓣。 **

那么谢揪,我們?nèi)绾卧趖estcase>teststep中引用api呢?如何引用testcase呢捐凭? **

V2.0案例學(xué)習(xí)

在teststep中可以通過api字段引用api definition 拨扶、通過testcase字段引用testcase。下面我們根據(jù)腳手架快速創(chuàng)建一個項目httprunner --startproject hruntest2.0(需要確保安裝的是httpruner3.0以下的版本)來說明:

$ httprunner --startproject hruntest2.0 && tree hruntest2.0 
Start to create new project: hruntest2.0
CWD: /Users/boyizhang/PycharmProjects/apitest

created folder: hruntest2.0
created folder: hruntest2.0/api
created folder: hruntest2.0/testcases
created folder: hruntest2.0/testsuites
created folder: hruntest2.0/reports
created file: hruntest2.0/api/demo_api.yml
created file: hruntest2.0/testcases/demo_testcase.yml
created file: hruntest2.0/testsuites/demo_testsuite.yml
created file: hruntest2.0/debugtalk.py
created file: hruntest2.0/.env
created file: hruntest2.0/.gitignore

hruntest2.0
├── api
│   └── demo_api.yml
├── debugtalk.py
├── reports
├── testcases
│   └── demo_testcase.yml
└── testsuites
    └── demo_testsuite.yml
  • api definition
# api/demo_api.yml
name: demo api
variables:
    var1: value1
    var2: value2
request:
    url: /api/path/$var1
    method: POST
    headers:
        Content-Type: "application/json"
    json:
        key: $var2
validate:
    - eq: ["status_code", 200]
  • testcase
config:
    name: "demo testcase"
    variables:
        device_sn: "ABC"
        username: ${ENV(USERNAME)}
        password: ${ENV(PASSWORD)}
    base_url: "http://127.0.0.1:5000"

teststeps:
-
    name: demo step 1
    api: path/to/api1.yml
    variables:
        user_agent: 'iOS/10.3'
        device_sn: $device_sn
    extract:
        - token: content.token
    validate:
        - eq: ["status_code", 200]

在teststep中使用api選項來引用api definition茁肠,執(zhí)行testcase/demo_testcase.yml用例文件患民。(注:api definition不屬于testcase范疇,無法直接執(zhí)行api/下的yml/json文件)垦梆。在teststep中傳的字段的優(yōu)先級會比api definition下的高匹颤,也就是說仅孩,如果teststep傳某個字段,那么會有限使用teststep傳的那個惋嚎,如果沒有杠氢,則再使用api definition的字段站刑。


V3.0

為了簡單另伍,在HttpRunner v2.x中的API概念已經(jīng)被取消了〗事茫可以將API定義為只有一個請求步驟的測試用例(需要重點注意一下這里)摆尝。

測試用例分層模型

  • 去掉api的概念,把api的概念轉(zhuǎn)為測試用例的概念因悲。將API定義為只有一個請求步驟的測試用例堕汞。以統(tǒng)一的概念:測試用例,使得維護用例的工作更加方便晃琳。
  • 按照V2.0的邏輯讯检,需要維護api definition,也需要維護只有一個測試步驟的testcase卫旱,而V3.0中人灼,我們只需要維護testcase即可。v2->v3的轉(zhuǎn)變顾翼,使得可以在避免重復(fù)描述的同時投放,解決測試用例的依賴關(guān)系,從而保證每個測試用例都是獨立可運行的适贸。

那么灸芳,問題來了,我們應(yīng)該如何在某個testcase的teststep中引用其他testcase呢拜姿?

v3.0案例學(xué)習(xí)

在測試步驟(teststep)中烙样,可通過 testcase 字段引用其它測試用例,引用方式為對應(yīng)測試用例文件的路徑蕊肥,絕對路徑或相對路徑均可谒获。推薦使用相對路徑,路徑基準(zhǔn)為項目根目錄晴埂,即 debugtalk.py 所在的目錄路徑究反。

**通過 **httprunner startproject hruntest3.0 快速創(chuàng)建項目。

$ tree hruntest3.0 
hruntest3.0
├── debugtalk.py
├── har
├── reports
└── testcases
    ├── demo_testcase_ref.yml
    └── demo_testcase_request.yml

# demo_testcase_request.yml
config:
    name: "request methods testcase with functions"
    variables:
        foo1: config_bar1
        foo2: config_bar2
        expect_foo1: config_bar1
        expect_foo2: config_bar2
    base_url: "https://postman-echo.com"
    verify: False
    export: ["foo3"]

teststeps:
-
    name: post form data
    variables:
        foo2: bar23
    request:
        method: POST
        url: /post
        headers:
            User-Agent: HttpRunner/${get_httprunner_version()}
            Content-Type: "application/x-www-form-urlencoded"
        data: "foo1=$foo1&foo2=$foo2&foo3=$foo3"
    validate:
        - eq: ["status_code", 200]
        - eq: ["body.form.foo1", "$expect_foo1"]
        - eq: ["body.form.foo2", "bar23"]
        - eq: ["body.form.foo3", "bar21"]

# demo_testcase_ref.yml 
config:
    name: "request methods testcase: reference testcase"
    variables:
        foo1: testsuite_config_bar1
        expect_foo1: testsuite_config_bar1
        expect_foo2: config_bar2
    base_url: "https://postman-echo.com"
    verify: False

teststeps:
-
    name: request with functions
    variables:
        foo1: testcase_ref_bar1
        expect_foo1: testcase_ref_bar1
    testcase: testcases/demo_testcase_request.yml
    export:
        - foo3
-
    name: post form data
    variables:
        foo1: bar1
    request:
        method: POST
        url: /post
        headers:
            User-Agent: HttpRunner/${get_httprunner_version()}
            Content-Type: "application/x-www-form-urlencoded"
        data: "foo1=$foo1&foo2=$foo3"
    validate:
        - eq: ["status_code", 200]
        - eq: ["body.form.foo1", "bar1"]
        - eq: ["body.form.foo2", "bar21"]

在teststep中使用testcase字段來引用儒洛,執(zhí)行demo_testcase_ref.yml用例文件精耐,當(dāng)執(zhí)行到name = request with functions這個步驟的時候會先執(zhí)行其引用的testcase:testcases/demo_testcase_request.yml。 在根目錄下琅锻,執(zhí)行hrun testcases/demo_testcase_ref.yml卦停,可以看到向胡,程序也生成了該case:testcases/demo_testcase_request.yml的測試文件:

hook機制

涉及hook處理

背景

在自動化測試中,執(zhí)行用例前惊完,需要執(zhí)行一些預(yù)處理操作僵芹,執(zhí)行用例后,需要做一些清理工作小槐。如果手動去操作的話拇派,就不是很合適。所以就需要用到hook機制凿跳,在執(zhí)行用例前后執(zhí)行hook函數(shù)件豌。

使用

hook 機制分為兩個層級:

  • 測試用例層面(testcase)
    • 在測試用例層面,主要在config字段新增兩個關(guān)鍵字 setup_hooks 和 teardown_hooks控嗜。在這里茧彤,其主要目的就是用于測試前的準(zhǔn)備工作以及測試后的清理工作。
  • 測試步驟層面(teststep)
    • 在每個teststep中新增關(guān)鍵字 setup_hooks 和 teardown_hooks疆栏。實現(xiàn)對請求的 request 內(nèi)容進(jìn)行預(yù)處理以及實現(xiàn)對響應(yīng)的 response 進(jìn)行修改曾掂。

編寫hook函數(shù)

# ${print_request($request)}
2021-07-18 09:30:46.740 | DEBUG    | httprunner.runner:__call_hooks:121 - call hook function: ${print_reqeust($request)}
{'method': 'GET', 'url': '/get', 'params': {'foo1': 'bar11', 'foo2': 'bar21', 'sum_v': 3}, 'headers': {'User-Agent': 'HttpRunner/3.1.5', 'HRUN-Request-ID': 'HRUN-7768261f-0abf-4ce5-abf2-06327de85fd7-846739'}, 'req_json': None, 'data': None, 'cookies': {}, 'timeout': 120, 'allow_redirects': True, 'verify': False}
# ${print_req($response)}
2021-07-18 09:36:03.019 | DEBUG    | httprunner.runner:__call_hooks:121 - call hook function: ${print_req($response)}
<httprunner.response.ResponseObject object at 0x109c87e50>

環(huán)境(.env)

涉及env的處理,文檔

# parser.py
def get_mapping_function(
    function_name: Text, functions_mapping: FunctionsMapping
) -> Callable:
    #省略
    if function_name in functions_mapping:
        return functions_mapping[function_name]

    elif function_name in ["environ", "ENV"]:
        return utils.get_os_environ
    #省略
    raise exceptions.FunctionNotFound(f"{function_name} is not found.")

# utils.py

def set_os_environ(variables_mapping):
    """ set variables mapping to os.environ
    """
    for variable in variables_mapping:
        os.environ[variable] = variables_mapping[variable]
        logger.debug(f"Set OS environment variable: {variable}")
        
def get_os_environ(variable_name):
    try:
        return os.environ[variable_name]
    except KeyError:
        raise exceptions.EnvNotFound(variable_name)

加載用例之前壁顶,會先把.env文件中的變化加載到環(huán)境中珠洗,如果用例用含有ENV或者environ,則通過os.environ去讀取相應(yīng)的值博助。

參數(shù)化:

實現(xiàn)

HttpRunner 實現(xiàn)參數(shù)化數(shù)據(jù)驅(qū)動機制:debugtalk.com/post/httpru… HttpRunner 再議參數(shù)化數(shù)據(jù)驅(qū)動機制:debugtalk.com/post/httpru…

使用

需要注意的是险污,從v2.0開始,參數(shù)化只支持在 testsuite 中實現(xiàn)富岳。不再支持在測試用例文件中進(jìn)行參數(shù)化配置蛔糯。

參數(shù)配置概述

  • 獨立參數(shù):指的是不與其他參數(shù)有任何關(guān)聯(lián)關(guān)系,與其他參數(shù)是相互獨立的窖式。
  • 關(guān)聯(lián)參數(shù):假如測試用例中定義了多個參數(shù)蚁飒,那么測試用例在運行時會對參數(shù)進(jìn)行笛卡爾積組合,覆蓋所有參數(shù)組合情況萝喘。

extract/export

底層使用 jmespath淮逻,提取和驗證json響應(yīng),使得提取更簡單阁簸。 extract用來抓取響應(yīng)體中的字段爬早,export導(dǎo)出當(dāng)前用例抓取的字段,供引用該用例的用例使用启妹。

測試報告

  • pytest內(nèi)置報告
    • hrun /path/to/testcase --html=report.html
  • allure報告
    • pip install allure-pytest
    • 執(zhí)行:hrun /path/to/testcase --alluredir=/tmp/my_allure_results
    • 在線打開報告:allure serve /tmp/my_allure_results 或者 生成html報告:allure generate reports/allure -o reports/allure/html

總結(jié)

好好利用httprunner筛严,可以覆蓋80%的場景,可以說是一個不錯的工具饶米,當(dāng)然我們除了學(xué)習(xí)應(yīng)該如何使用之外桨啃,更應(yīng)該學(xué)習(xí)人家設(shè)計的思想车胡,這樣自己之后才能有機會做好一點的工具。 另外推薦一個Java版本的接口自動化工具大家可以參考rest-assured照瘾。httprunner與rest-assured 實現(xiàn)方式大同小異匈棘,不過 rest-assured 并沒有通過json/yaml去管理用例,而是直接寫測試文件(類似httprunner生成的py文件)析命。大家可以對比學(xué)習(xí)下~主卫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碳却,隨后出現(xiàn)的幾起案子队秩,更是在濱河造成了極大的恐慌笑旺,老刑警劉巖昼浦,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筒主,居然都是意外死亡关噪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門乌妙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來使兔,“玉大人,你說我怎么就攤上這事藤韵∨傲ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵泽艘,是天一觀的道長欲险。 經(jīng)常有香客問我,道長匹涮,這世上最難降的妖魔是什么天试? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮然低,結(jié)果婚禮上喜每,老公的妹妹穿的比我還像新娘。我一直安慰自己雳攘,他們只是感情好带兜,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吨灭,像睡著了一般刚照。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沃于,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天涩咖,我揣著相機與錄音海诲,去河邊找鬼。 笑死檩互,一個胖子當(dāng)著我的面吹牛特幔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闸昨,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼蚯斯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饵较?” 一聲冷哼從身側(cè)響起拍嵌,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎循诉,沒想到半個月后横辆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡茄猫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年狈蚤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片划纽。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡脆侮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勇劣,到底是詐尸還是另有隱情靖避,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布比默,位于F島的核電站幻捏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏退敦。R本人自食惡果不足惜粘咖,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侈百。 院中可真熱鬧瓮下,春花似錦、人聲如沸钝域。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽例证。三九已至路呜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胀葱。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工漠秋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抵屿。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓庆锦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轧葛。 傳聞我的和親對象是個殘疾皇子搂抒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容