Pytest官方教程-20-编写钩子(hook)方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pytest官方教程-20-编写钩子(hook)方法相关的知识,希望对你有一定的参考价值。

参考技术A

pytest为任何给定的钩子(hook)规范调用已注册插件的钩子(hook)方法。让我们看一下钩子(hook)的典型钩子(hook)方法,pytest在收集完所有测试项目后调用。 pytest_collection_modifyitems(session, config,items)

当我们 pytest_collection_modifyitems 在插件中实现一个方法时,pytest将在注册期间验证你是否使用了与规范匹配的参数名称,如果没有则拯救。

让我们看一下可能的实现:

这里, pytest 将传入 config (pytest配置对象)和 items (收集的测试项列表),但不会传入 session 参数,因为我们没有在方法签名中列出它。这种动态的“修剪”参数允许 pytest “未来兼容”:我们可以引入新的钩子(hook)命名参数而不破坏现有钩子(hook)实现的签名。这是pytest插件的一般长期兼容性的原因之一。

请注意,除了 pytest_runtest_* 不允许引发异常之外的钩子(hook)方法。这样做会打破pytest运行。

大多数对 pytest 钩子(hook)的调用都会产生一个 结果列表, 其中包含被调用钩子(hook)方法的所有非None结果。

一些钩子(hook)规范使用该 firstresult=True 选项,以便钩子(hook)调用仅执行,直到N个注册方法中的第一个返回非None结果,然后将其作为整个钩子(hook)调用的结果。在这种情况下,不会调用其余的钩子(hook)方法。

版本2.7中的新功能。

pytest插件可以实现钩子(hook)包装器,它包装其他钩子(hook)实现的执行。钩子(hook)包装器是一个生成器方法,它只产生一次。当pytest调用钩子(hook)时,它首先执行钩子(hook)包装器并传递与常规钩子(hook)相同的参数。

在钩子(hook)包装器的屈服点,pytest将执行下一个钩子(hook)实现,并以 Result 封装结果或异常信息的实例的形式将其结果返回到屈服点。因此,屈服点本身通常不会引发异常(除非存在错误)。

以下是钩子(hook)包装器的示例定义:

请注意,钩子(hook)包装器本身不返回结果,它们只是围绕实际的钩子(hook)实现执行跟踪或其他副作用。如果底层钩子(hook)的结果是一个可变对象,它们可能会修改该结果,但最好避免它。

有关更多信息,请参阅 插件文档 。

对于任何给定的钩子(hook)规范,可能存在多个实现,因此我们通常将 hook 执行视为 1:N 方法调用,其中 N 是已注册方法的数量。有一些方法可以影响钩子(hook)实现是在其他人之前还是之后,即在 N -sized方法列表中的位置:

这是执行的顺序:

这是可能的使用 tryfirst ,并 trylast 结合还 hookwrapper=True 处于这种情况下,它会影响彼此之间hookwrappers的排序。

插件和 conftest.py 文件可以声明新钩子(hook),然后可以由其他插件实现,以便改变行为或与新插件交互:

<dt> pytest_addhooks (*pluginmanager *) [来源]

在插件注册时调用,允许通过调用添加新的挂钩 。 pluginmanager.add_hookspecs(module_or_class, prefix)

参数: | pluginmanager ( _pytest.config.PytestPluginManager ) - pytest插件管理器

钩子(hook)通常被声明为do-nothing方法,它们只包含描述何时调用钩子(hook)以及期望返回值的文档。

有关示例,请参阅 xdist中 的 newhooks.py 。

由于标准的 验证机制, 如上所述使用插件中的新钩子(hook)可能有点棘手:如果你依赖未安装的插件,验证将失败并且错误消息对你的用户没有多大意义。

一种方法是将钩子(hook)实现推迟到新的插件,而不是直接在插件模块中声明钩子(hook)方法,例如:

pytest + yaml 框架 -6.hooks 钩子功能实现

前言

在发送请求的时候,我们希望在发送请求参数前,带上签名的值,或者返回的内容需要二次处理,解密后返回。
此功能我们可以用 hooks 钩子来实现
pip 安装插件

pip install pytest-yaml-yoyo

hooks 功能在v1.0.4版本上实现

response 钩子功能

requests 库只支持一个 response 的钩子,即在响应返回时可以捎带执行我们自定义的某些方法。
可以用于打印一些信息,做一些响应检查或想响应对象中添加额外的信息

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
import requests
url = 'https://httpbin.org/get'


def response_status(resopnse, *args, **kwargs):
    print('url', resopnse.url)
    resopnse.status = 'PASS' if resopnse.status_code < 400 else 'FAIL'


res = requests.get(url, hooks='response': response_status)
print(res.status)

以上是基于requests 库的钩子功能实现的基本方式

yaml 用例中添加response 钩子

在yaml 文件中添加response 钩子功能,跟上面代码方式差不多, 有2种方式

  • 1.写到config 全局配置,每个请求都会带上hooks
  • 2.写到单个请求的request 下,仅单个请求会带上hooks功能

先看单个请求的response 钩子
test_hook1.yml

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
config:
  name: post示例
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]

在conftest.py 中注册钩子函数

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

def hook_response(response, *args, **kwargs):
    # print(response.text) 原始数据
    print("执行response hook 函数内容....")
    class NewResponse:
        text = '"code": 0, "data": "token": "yo yo"'  # response.text解密
        history = response.history
        headers = response.headers
        cookies = response.cookies
        status_code = response.status_code
        raw = response.raw
        is_redirect = response.is_redirect
        content = b'"code": 0, "data": "token": "yo yo"'  # response.text解密
        elapsed = response.elapsed

        @staticmethod
        def json():
            # 拿到原始的response.json() 后解码
            return "code": 0, "data": "token": "yo yo"

    return NewResponse

my_builtins.hook_response = hook_response

由于上面用例只在第一个请求中使用了hooks功能,所以第二个请求的断言- eq: [$.code, 0] 会失败

钩子方法调用语法

  • 1.层级是在request 下
  • 2.hooks 关键字对应的是一个字典 “response”: []
  • 3.response 的值可以是单个函数名称,也可以是多个func1, func2,或者是一个list类型[func1, func2]
  • 4.response 的值必须是一个可以调用的函数,此函数需在conftest 中注册绑定到my_builtins 模块
  • 5.调用的函数第一个参数是response, 可以重写response内容(如需要对返回结果解密),也可以不用重写
  request:
    method: POST
    url: http://httpbin.org/post
    hooks:
      response: ['hook_response']

config 全局使用

在config 中配置全局hooks功能,格式如下

config:
  name: post示例
  hooks:
    response: ['hook_response']

test_hook2.yml完整示例

config:
  name: post示例
  hooks:
    response: ['hook_response']
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]

全局配置hooks, 那么该用例下所有的请求都会带上hooks

请求预处理钩子

如果需要对请求参数预处理,我们还新增了一个request 请求钩子,可以获取到发送请求时的request参数

在conftest.py

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/


def func1(req):
    print(f'请求预处理:req')
    
    
def func2():
    print(f'请求预处理-----------')
    
    
my_builtins.func1 = func1
my_builtins.func2 = func2

在 yaml 文件中使用示例

config:
  name: post示例
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      request: ['func1', 'func2']
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]

在config 中设置全局hooks示例

config:
  name: post示例
  hooks:
    request: ['func1', 'func2']
    response: ['hook_response']

由于request 变量是pytest的一个内置fixture,此变量保留着,获取请求参数的函数使用req 代替。
利用request hook功能可以实现请求参数的预处理,比如请求body签名和加密处理等需求。

以上是关于Pytest官方教程-20-编写钩子(hook)方法的主要内容,如果未能解决你的问题,请参考以下文章

Cypress系列- Cypress 编写和组织测试用例篇 之 钩子函数Hook

Cypress系列- Cypress 编写和组织测试用例篇 之 钩子函数Hook

pytest + yaml 框架 -6.hooks 钩子功能实现

40-pytest-Hook函数之参数化生成用例

pytest文档33-Hooks函数获取用例执行结果(pytest_runtest_makereport)

pytest文档33-Hooks函数获取用例执行结果(pytest_runtest_makereport)