httprunner3.x总结15 - 钩子函数hook(常用测试前置及后置)

HttpRunner 实现 hook 机制:
https://www.dazhuanlan.com/2019/11/23/5dd8ce9e262a7/

可以在请求前和请求后调用钩子函数,相当于unittest的setup和teardown,测试前置,测试后置
hook 机制分为两个层级:测试用例层面(testcase)、测试步骤层面(teststep)

(1)测试步骤层面:

# NOTE: Generated By HttpRunner v3.1.4
# FROM: hooks.yml


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


class TestCaseHooks(HttpRunner):

    config = Config("basic test").base_url("${get_httpbin_server()}")

    teststeps = [
        Step(
            RunRequest("headers")
            .with_variables(**{"a": 123})
            .setup_hook("${setup_hook_add_kwargs($request)}")
            .get("/headers")
            .teardown_hook("${teardown_hook_sleep_N_secs($response, 1)}")
            .validate()
            .assert_equal("status_code", 200)
            .assert_contained_by("body.headers.Host", "${get_httpbin_server()}")
        ),
    ]


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

debugtalk.py

import requests
import os, time
from bs4 import BeautifulSoup
from httprunner import __version__
from libs.model_001 import *
from libs.model_002 import *
import json


# 可以实现根据请求方法和请求的 Content-Type 来对请求的 data 进行加工处理;
def setup_hook_prepare_kwargs(request):
    if request["method"] == "POST":
        content_type = request.get("headers", {}).get("content-type")
        if content_type and "data" in request:
            # if request content-type is application/json, request data should be dumped
            if content_type.startswith("application/json") and isinstance(request["data"], (dict, list)):
                request["data"] = json.dumps(request["data"])

            if isinstance(request["data"], str):
                request["data"] = request["data"].encode('utf-8')

# 可以实现 HttpNtlmAuth 权限授权。
def setup_hook_httpntlmauth(request):
    if "httpntlmauth" in request:
        from requests_ntlm import HttpNtlmAuth
        auth_account = request.pop("httpntlmauth")
        request["auth"] = HttpNtlmAuth(
            auth_account["username"], auth_account["password"])

# 根据接口响应的状态码来进行不同时间的延迟等待。
def teardown_hook_sleep_N_secs(response, n_secs):
    """ sleep n seconds after request
    """
    if response.status_code == 200:
        time.sleep(0.1)
    else:
        time.sleep(n_secs)

# 当我们需要先对响应内容进行处理(例如加解密、参数运算),再进行参数提取(extract)和校验(validate)时尤其有用
# 将响应结果的状态码和 headers 进行了修改,然后再进行了校验。
def alter_response(response):
    response.status_code = 500
    response.headers["Content-Type"] = "html/text"

def get_execution(host):
    # 从get登录的html中获取post登录的变量值,传递给登录
    res = requests.get(url=f"https://{host}:8888/cas/login", verify=False)
    execution = BeautifulSoup(res.text, "lxml").find(attrs={"name":"execution"})["value"]
    return execution

def get_httprunner_version():
    return __version__

# 返回一个列表
def get_user_id():
    return [
        {"user_id": 1001},
        {"user_id": 1002},
        {"user_id": 1003},
        {"user_id": 1004}
    ]

# 具有关联性的多个参数
def get_account(num):
    accounts = []
    for index in range(1, num+1):
        accounts.append(
            {"username": "user%s" % index, "password": str(index) * 6},
        )
    return accounts
    

if __name__ == '__main__':
    get_execution()

(1)用例级别的测试前置

class TestBaiduRequestTestCase(HttpRunner):  
    def setup(self):  
        print("运行于测试用例之前")  
  
    def teardown(self):  
        print("运行于测试用例之后")  
  
    config = (  
        Config("get user list")  
        .base_url("https://www.baidu.com")  
        .verify(False)  
    )  
  
    teststeps = [  
        Step(  
            RunRequest("get info")  
            .get("/")  
            .validate()  
            .assert_equal("status_code", 200)  
        )  
    ]  
  
  
if __name__ == "__main__":  
    TestBaiduRequestTestCase().test_start()  


结果为:

Process finished with exit code 0  
运行于测试用例之前  
PASSED [100%]2020-08-20 13:50:53.306 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from D:\TestScriptDir\httprunner\interfaceDemo\.env  
.  
.  
.  
 D:\TestScriptDir\httprunner\interfaceDemo\logs\a3872c1b-dedf-4485-bd95-3f31947bfae0.run.log  
运行于测试用例之后  

(1)用例级别的测试前置confest.py

import uuid
from typing import List

import pytest
from httprunner import Config, Step
from loguru import logger


@pytest.fixture(scope="session", autouse=True)
def session_fixture(request):
    """setup and teardown each task"""
    total_testcases_num = request.node.testscollected
    testcases = []
    for item in request.node.items:
        testcase = {
            "name": item.cls.config.name,
            "path": item.cls.config.path,
            "node_id": item.nodeid,
        }
        testcases.append(testcase)

    logger.debug(f"collected {total_testcases_num} testcases: {testcases}")

    yield

    logger.debug(f"teardown task fixture")

    # teardown task
    # TODO: upload task summary


@pytest.fixture(scope="function", autouse=True)
def testcase_fixture(request):
    """setup and teardown each testcase"""
    config: Config = request.cls.config
    teststeps: List[Step] = request.cls.teststeps

    logger.debug(f"setup testcase fixture: {config.name} - {request.module.__name__}")

    def update_request_headers(steps, index):
        for teststep in steps:
            if teststep.request:
                index += 1
                teststep.request.headers["X-Request-ID"] = f"{prefix}-{index}"
            elif teststep.testcase and hasattr(teststep.testcase, "teststeps"):
                update_request_headers(teststep.testcase.teststeps, index)

    # you can update testcase teststep like this
    prefix = f"HRUN-{uuid.uuid4()}"
    update_request_headers(teststeps, 0)

    yield

    logger.debug(
        f"teardown testcase fixture: {config.name} - {request.module.__name__}"
    )

    summary = request.instance.get_summary()
    logger.debug(f"testcase result summary: {summary}")

    # TODO: upload testcase summary
# NOTICE: Generated By HttpRunner.
import json
import os
import time

import pytest
from loguru import logger

from httprunner.utils import get_platform, ExtendJSONEncoder


@pytest.fixture(scope="session", autouse=True)
def session_fixture(request):
    """setup and teardown each task"""
    logger.info(f"start running testcases ...")

    start_at = time.time()

    yield

    logger.info(f"task finished, generate task summary for --save-tests")

    summary = {
        "success": True,
        "stat": {
            "testcases": {"total": 0, "success": 0, "fail": 0},
            "teststeps": {"total": 0, "failures": 0, "successes": 0},
        },
        "time": {"start_at": start_at, "duration": time.time() - start_at},
        "platform": get_platform(),
        "details": [],
    }

    for item in request.node.items:
        testcase_summary = item.instance.get_summary()
        summary["success"] &= testcase_summary.success

        summary["stat"]["testcases"]["total"] += 1
        summary["stat"]["teststeps"]["total"] += len(testcase_summary.step_datas)
        if testcase_summary.success:
            summary["stat"]["testcases"]["success"] += 1
            summary["stat"]["teststeps"]["successes"] += len(
                testcase_summary.step_datas
            )
        else:
            summary["stat"]["testcases"]["fail"] += 1
            summary["stat"]["teststeps"]["successes"] += (
                len(testcase_summary.step_datas) - 1
            )
            summary["stat"]["teststeps"]["failures"] += 1

        testcase_summary_json = testcase_summary.dict()
        testcase_summary_json["records"] = testcase_summary_json.pop("step_datas")
        summary["details"].append(testcase_summary_json)

    summary_path = "/Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/postman_echo/logs/request_methods/hardcode.summary.json"
    summary_dir = os.path.dirname(summary_path)
    os.makedirs(summary_dir, exist_ok=True)

    with open(summary_path, "w", encoding="utf-8") as f:
        json.dump(summary, f, indent=4, ensure_ascii=False, cls=ExtendJSONEncoder)

    logger.info(f"generated task summary: {summary_path}")

日志库:loguru
解读:https://www.jb51.net/article/182175.htm


# 日志库基础应用
from loguru import logger

logger.add("interface_log_{time}.log", rotation="500MB", encoding="utf-8", enqueue=True, compression="zip",
           retention="10 days")
logger.info("中文test")
logger.debug("中文test")
logger.error("中文test")
logger.warning("中文test")
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿_焦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值