httprunner學習實踐(五)-- skip機制和hook機制

序言

httprunner的使用和其中的一些概念在官方的文檔中已經(jīng)詳細說明赎瑰,寫這個學習記錄是為了記錄下自己的學習歷程羊瘩,能夠在今后快速的深入學習和實踐俩檬。沒有提及的部分要糊,請查看官方文檔
版本:2.2.5

參考

HttpRunner V2.x 中文使用文檔

skip機制

skip類型

  • skip: 無條件跳過測試
  • skipIf: 判斷結(jié)果返回為true時纲熏,跳過測試
  • skipUnless:判斷結(jié)果返回為false時,跳過測試

使用方法

api中增加skip節(jié)點或在testcaseteststeps增加skip節(jié)點杨耙。

  • skip跳過測試,值為跳過測試的說明
-
    api: api/sale/order_products.yml
    skip: 無條件跳過
  • skipIf跳過測試赤套,值為驗證方法,方法在debugtalk.py中定義
    debugtalk.py
def varIsNormal(var):
    if var:
        return False
    return True

testcase

-
    api: api/sale/order_list.yml
    skipIf: ${varIsNormal($token)}
  • skipUnless跳過測試珊膜,值為驗證方法容握,方法在debugtalk.py中定義
-
    api: api/sale/order_detail.yml
    skipUnless: ${varIsNormal($token)}

運行機制,如果判斷test中有skip车柠,skipIfskipUnless剔氏,拋出SkipTest異常。

    def _handle_skip_feature(self, test_dict):
        """ handle skip feature for test
            - skip: skip current test unconditionally
            - skipIf: skip current test if condition is true
            - skipUnless: skip current test unless condition is true

        Args:
            test_dict (dict): test info

        Raises:
            SkipTest: skip test

        """
        # TODO: move skip to initialize
        skip_reason = None

        if "skip" in test_dict:
            skip_reason = test_dict["skip"]

        elif "skipIf" in test_dict:
            skip_if_condition = test_dict["skipIf"]
            if self.session_context.eval_content(skip_if_condition):
                skip_reason = "{} evaluate to True".format(skip_if_condition)

        elif "skipUnless" in test_dict:
            skip_unless_condition = test_dict["skipUnless"]
            if not self.session_context.eval_content(skip_unless_condition):
                skip_reason = "{} evaluate to False".format(skip_unless_condition)

        if skip_reason:
            raise SkipTest(skip_reason)

hook機制 ?

類型

setup_hooks:請求前調(diào)用的鉤子函數(shù)竹祷,對請求進行預處理
teardown_hooks:請求之后調(diào)用的鉤子函數(shù)谈跛,對請求進行后置處理

場景

對testcase生效的鉤子
  • testcaseconfig中使用的hook函數(shù),函數(shù)在debugtalk.py中自由定義塑陵,他的運行是相對于整個測試用例的
    setup_hooks: 在整個用例開始執(zhí)行前觸發(fā) hook 函數(shù)感憾,主要用于準備工作
    teardown_hooks: 在整個用例結(jié)束執(zhí)行后觸發(fā) hook 函數(shù),主要用于測試后的清理工作
config:
    name: 開發(fā)者登錄后注冊
    base_url: https://api.apiopen.top
    variables:
        developer_name: o_chen
        passwd: 123456
        success_code: 200
    setup_hooks:
        - ${sleep(30)}
    teardown_hooks:
        - ${sleep(30)}
    output:
        - apikey

teststeps:
-
    name: 開發(fā)者登錄
    api: api/apiopen/developer_login.yml
def sleep(n_secs):
    time.sleep(n_secs)

查看運行日志


用例運行之前

用例運行之后
  • teststeps引用testcase,也可以在節(jié)點中定義hook令花,是對引用的testcase生效的
config:
    name: 開發(fā)者登錄后注冊
    base_url: https://api.apiopen.top
    variables:
        developer_name: o_chen
        passwd: 123456
        success_code: 200

teststeps:
-
    name: 開發(fā)者登錄
    testcase: testcases/apiopen/login.yml
    setup_hooks:
        - ${sleep(30)}
    teardown_hooks:
        - ${sleep(30)}
對api生效的鉤子

api文件中添加hooks節(jié)點阻桅,將在測試之前和測試之后運行
運行測試時,httprunner會把當前執(zhí)行的請求和返回儲存當變量中兼都,取名requestresponse.當需要對請求和返回做處理時傳入對應的變量名稱即可.當然嫂沉,也可以自由的執(zhí)行其他的函數(shù),在debugtalk.py中編寫函數(shù)扮碧,在hooks中添加調(diào)用就行了趟章。

name: 開發(fā)者登錄
base_url: https://api.apiopen.top
variables:
    developer_name: dev
    passwd: 123456
    success_code: 200
request:
    method: POST
    url: /developerLogin
    data:
        name: $developer_name
        passwd: $passwd
extract:
    apikey: content.result.apikey
validate:
    - eq: [content.code,$success_code]
setup_hooks:
    - ${hook_print($request)}
    - ${sum_two(1,2)}
teardown_hooks:
    - ${hook_print($response)}

運行結(jié)果:


在test之前運行

testcase中對引入的api文件添加hooks,將會與原來的文件中的hooks進行合并慎王、覆蓋

config:
    name: 開發(fā)者登錄
    base_url: https://api.apiopen.top
    variables:
        developer_name: o_chen
        passwd: 123456
        success_code: 200

teststeps:
-
    name: 開發(fā)者登錄
    api: api/developer_login.yml
    setup_hooks:
        - ${sum_two(1,2)}
    teardown_hooks:
        - ${sum_two(3,4)}

運行結(jié)果:


合并之后的setup_hooks

合并之后的teardown_hooks

引用

編寫 hook 函數(shù) ?

hook 函數(shù)的定義放置在項目的 debugtalk.py 中蚓土,在 YAML/JSON 中調(diào)用 hook 函數(shù)仍然是采用 ${func($a, $b)} 的形式。

setup_hooks ?

在測試步驟層面的 setup_hooks 函數(shù)中赖淤,除了可傳入自定義參數(shù)外北戏,還可以傳
$request,該參數(shù)對應著當前測試步驟 request 的全部內(nèi)容漫蛔。因為 request 是可變
參數(shù)類型(dict)嗜愈,因此該函數(shù)參數(shù)為引用傳遞,當我們需要對請求參數(shù)進行預處理時尤其有用莽龟。

teardown_hooks ?

在測試步驟層面的 teardown_hooks 函數(shù)中蠕嫁,除了可傳入自定義參數(shù)外,還可以傳入 $response毯盈,該參數(shù)對應著當前請求的響應實例(requests.Response)
另外剃毒,在 teardown_hooks 函數(shù)中還可以對 response 進行修改。當我們需要先對響應內(nèi)容進行處理(例如加解密搂赋、參數(shù)運算)赘阀,再進行參數(shù)提取(extract)和校驗(validate)時尤其有用脑奠。

例:
定義hooks函數(shù)修改請求參數(shù)和返回數(shù)據(jù)

def setupRequest(request):
    if request:
        if "data" in request:
            request["data"]["name"] = "O_cheng"

def teardownResponse(response):
    if response:
        # print(response.json)
        # print(response.content)
        # print(response.status_code)
        # print(response.cookies)
        # print(response.elapsed)
        # print(response.encoding)
        # print(response.headers)
        # print(response.history)
        # print(response.ok)
        # print(response.text)
        # print(response.url)
        # print(response.request)
        response.json["code"] = -10000

api腳本中設(shè)置hooks函數(shù)調(diào)用

name: 開發(fā)者登錄
base_url: https://api.apiopen.top
variables:
    developer_name: dev
    passwd: 123456
    success_code: 200
request:
    method: POST
    url: /developerLogin
    data:
        name: $developer_name
        passwd: $passwd
extract:
    apikey: content.result.apikey
validate:
    - eq: [content.code,$success_code]
setup_hooks:
    - ${setupRequest($request)}
teardown_hooks:
    - ${teardownResponse($response)}

運行腳本文件基公,查看輸出的日志:

  • 運行setup_hooks修改請求參數(shù)
    setup_hooks修改請求參數(shù)
  • 運行teardown_hooks修改返回參數(shù)
    teardown_hooks修改返回參數(shù)

擴展

在瀏覽源碼的時候,發(fā)現(xiàn)hooks支持將函數(shù)的運行結(jié)果賦值給變量宋欺。源碼如下:

    def do_hook_actions(self, actions, hook_type):
        """ call hook actions.

        Args:
            actions (list): each action in actions list maybe in two format.

                format1 (dict): assignment, the value returned by hook function will be assigned to variable.
                    {"var": "${func()}"}
                format2 (str): only call hook functions.
                    ${func()}

            hook_type (enum): setup/teardown

        """
        logger.log_debug("call {} hook actions.".format(hook_type))
        for action in actions:

            if isinstance(action, dict) and len(action) == 1:
                # format 1
                # {"var": "${func()}"}
                var_name, hook_content = list(action.items())[0]
                hook_content_eval = self.session_context.eval_content(hook_content)
                logger.log_debug(
                    "assignment with hook: {} = {} => {}".format(
                        var_name, hook_content, hook_content_eval
                    )
                )
                self.session_context.update_test_variables(
                    var_name, hook_content_eval
                )
            else:
                # format 2
                logger.log_debug("call hook function: {}".format(action))
                # TODO: check hook function if valid
                self.session_context.eval_content(action)

當定義hooks的格式為dict時可以實現(xiàn)將函數(shù)返回值添加到變量中

setup_hooks:
    - var: ${func($request)}

但在實際運行過程中轰豆,在httprunner檢查case時發(fā)生異常。具體問題如下: Issues
如果需要使用該功能齿诞,可以修改源碼酸休,以下修改僅供參考。

單獨運行api

api中編寫hook將返回值賦值給變量并引用變量報錯:httprunner.exceptions.VariableNotFound
修改源碼中parser.py,在第958行添加:

        hooks_mapping = {}
        if "setup_hooks" in test_dict:
            for hooks in test_dict["setup_hooks"]:
                if isinstance(hooks, dict):
                    hooks_mapping.update(hooks)

        if "teardown_hooks" in test_dict:
            for hooks in test_dict["teardown_hooks"]:
                if isinstance(hooks, dict):
                    hooks_mapping.update(hooks)
        session_variables_set |= set(hooks_mapping.keys())

hook中定義的變量名稱添加到teststep_variables_set中,teststep_variables_set用于校驗testcase中引用的變量是否存在祷杈,在測試之前檢驗testcase是否是完整的斑司,避免在測試過程中出現(xiàn)異常,檢查testcase完整性的操作將在運行test之前完成

運行testcase

如果在hook將函數(shù)的返回值賦值給變量但汞,那么他在hook中將以dict的形式定義如:

setup_hooks:
    - var: ${getRequest($request)}
    - req: ${getRequest($request)}
    - ${setupRequest($request)}
teardown_hooks:
    - ${teardownResponse($response)}
    - ${hook_print($var)}
    - ${hook_print($req)}

在運行測試之前宿刮,httprunner會將testcase中定義的hooks與api中定義的hooks進行合并去重,如果hooks中存在dict特占,將會報錯:TypeError: unhashable type: 'dict'.原因是合并hooks使用的是set()方法,在parser.py的第816行

    # merge & override setup_hooks
    def_setup_hooks = api_def_dict.pop("setup_hooks", [])
    ref_setup_hooks = test_dict.get("setup_hooks", [])
    extended_setup_hooks = list(set(def_setup_hooks + ref_setup_hooks))
    if extended_setup_hooks:
        test_dict["setup_hooks"] = extended_setup_hooks

修改之前首先要對合并hooks的方法重新編寫糙置,區(qū)分dict和普通的hooks,普通的hook合并后去重是目,dict形式的谤饭,相同key名稱時,testcase中定義的覆蓋api中原因的定義懊纳,順序為dict在前揉抵,執(zhí)行方法并返回值到變量,之后在執(zhí)行普通hook,他們有可能應用之前定義的變量嗤疯。
validator.py中添加方法

def extend_hooks(raw_hooks, override_hooks):
    """ extend raw_hooks with override_hooks.
        override_hooks will merge and override raw_hooks.

    Args:
        raw_hooks (list):
        override_hooks (list):

    Returns:
        list: extended hooks

    Examples:
        >>> def_setup_hooks = ["${hook_print($token)}", {"sign": "${getSign($token)}"}]
        >>> ref_setup_hooks = [{"sign": "${getRandomSign($token)}"}, "${hook_print($sign)}"]
        >>> extend_hooks(def_setup_hooks, ref_setup_hooks)
            [
                {'sign': '${getRandomSign($token)}'},
                '${hook_print($token)}',
                '${hook_print($sign)}'
            ]

    """
    extended_hooks_dict = {}
    extended_hooks_list = []

    def separation(hooks):
        for hook in hooks:
            if isinstance(hook, dict):
                extended_hooks_dict.update(hook)
            else:
                extended_hooks_list.append(hook)

    separation(raw_hooks)
    separation(override_hooks)

    extended_hooks = [{k: v} for k, v in extended_hooks_dict.items()]
    extended_hooks.extend(list(set(extended_hooks_list)))
    return extended_hooks

之后將parser.py中合并hooks的地方使用自定義的方法即可

    # merge & override setup_hooks
    def_setup_hooks = api_def_dict.pop("setup_hooks", [])
    ref_setup_hooks = test_dict.get("setup_hooks", [])
    # extended_setup_hooks = list(set(def_setup_hooks + ref_setup_hooks))
    extended_setup_hooks = validator.extend_hooks(def_setup_hooks, ref_setup_hooks)
    if extended_setup_hooks:
        test_dict["setup_hooks"] = extended_setup_hooks

    # merge & override teardown_hooks
    def_teardown_hooks = api_def_dict.pop("teardown_hooks", [])
    ref_teardown_hooks = test_dict.get("teardown_hooks", [])
    # extended_teardown_hooks = list(set(def_teardown_hooks + ref_teardown_hooks))
    extended_teardown_hooks = validator.extend_hooks(def_teardown_hooks, ref_teardown_hooks)
    if extended_teardown_hooks:
        test_dict["teardown_hooks"] = extended_teardown_hooks

將上述兩處修改完成后就可以在用例文件中將hooks返回的參數(shù)賦值給變量冤今,并在測試中使用這些變量了。


對變量進行賦值

引用setup_hooks中定義的變量
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茂缚,一起剝皮案震驚了整個濱河市戏罢,隨后出現(xiàn)的幾起案子屋谭,更是在濱河造成了極大的恐慌,老刑警劉巖龟糕,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桐磁,死亡現(xiàn)場離奇詭異,居然都是意外死亡讲岁,警方通過查閱死者的電腦和手機校摩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門舶治,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘫辩,你說我怎么就攤上這事坛悉≌豕欤” “怎么了卷扮?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長车遂。 經(jīng)常有香客問我澄港,道長祖搓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮该贾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘糠爬。我一直安慰自己殴玛,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布滚粟。 她就那樣靜靜地躺著寻仗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凡壤。 梳的紋絲不亂的頭發(fā)上署尤,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天耙替,我揣著相機與錄音,去河邊找鬼曹体。 笑死俗扇,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的箕别。 我是一名探鬼主播铜幽,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼串稀!你這毒婦竟也來了除抛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤母截,失蹤者是張志新(化名)和其女友劉穎到忽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體清寇,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡喘漏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了华烟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翩迈。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖垦江,靈堂內(nèi)的尸體忽然破棺而出帽馋,到底是詐尸還是另有隱情,我是刑警寧澤比吭,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布绽族,位于F島的核電站,受9級特大地震影響衩藤,放射性物質(zhì)發(fā)生泄漏吧慢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一赏表、第九天 我趴在偏房一處隱蔽的房頂上張望检诗。 院中可真熱鬧,春花似錦瓢剿、人聲如沸逢慌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攻泼。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忙菠,已是汗流浹背何鸡。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牛欢,地道東北人骡男。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像傍睹,于是被迫代替她去往敵國和親隔盛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

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