pytest测试框架4-插件与hook函数

一.简介

pytest的自带功能很强大,通过添加插件可以扩展功能,pytest的代码结构适合定制和扩展插件,

可以借助hook函数来实现。

把fixture函数或者hook函数添加到conftest文件里,就已经创建了一个本地的conftest插件!

 

二.pytest plugin加载的几种方式:

1.内置plugins:从代码内部的_pytest目录加载;

2.外部插件(第三方插件):通过setuptools entry points机制发现的第三方插件模块;

推荐的第三方的pytest的插件:https://docs.pytest.org/en/latest/plugins.html

3.conftest.py形式的本地插件:测试目录下的自动模块发现机制

通过pytest --trace-config命令可以查看当前pytest中所有的plugin。

在pytest中,所谓plugin其实就是能被pytest发现的一些带有pytest hook方法的文件或对象。

 

三.What is a hook

要理解pytest hook,首先要知道什么是hook方法(钩子函数)。

这里举一个简单的例子,比如说你写了一个框架类的程序,然后你希望这个框架可以“被代码注入”,即别人可以加入代码对你这个框架进行定制化,该如何做比较好?一种很常见的方式就是约定一个规则,框架初始化时会收集满足这个规则的所有代码(文件),然后把这些代码加入到框架中来,在执行时一并执行即可。所有这一规则下可以被框架收集到的方法就是hook方法。

 

四.编写自己的插件

插件可以改变pytest行为,可用的hook函数很多,详细的定义:

http://doc.pytest.org/en/latest/_modules/_pytest/hookspec.html

 

1.pytest_addoption为例,基本每个pytest plugin都会有这个hook方法,它的作用是为pytest命令行添加自定义的参数。

parser:用户命令行参数与ini文件值的解析器

def pytest_addoption(parser):
    parser.addoption("--env",    ##注册一个命令行选项
                    default="test",
                    dest="env",
                    help="set test run env")

pytest_addoption: Hook function, 这里创建了一个argparser的group,通过addoption方法添加option,使得显示help信息时相关option显示在一个group下面,更加友好。

命令行输入:

pytest --help 就可以看到

 

2.修改pytest_collection_modifyitems

能解决什么实际问题?

测试case中 case名字为中文时,显示的时乱码!

 

完成所有测试项的收集后,pytest调用的钩子

def pytest_collection_modifyitems(items):
    """
    测试用例收集完成时,将收集到的item的name和nodeid的中文显示在控制台上
所有的测试用例收集完毕后调用, 可以再次过滤或者对它们重新排序
 items (收集的测试项目列表)
    """
    for item in items:
        item.name = item.name.encode("utf-8").decode("unicode_escape")
        item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape") 

3.可以实现自己的自定义动态参数化方案或扩展

def pytest_generate_tests(metafunc):
    #""" generate (multiple) parametrized calls to a test function."""
    if "param" in metafunc.fixturenames:
        metafunc.parametrize("param",metafunc.module.par_to_test,ids=metafunc.module.case, scope="function")

然后测试用例的编写如下:

import pytest
import requests
 
from utils.get_data import get_data_path
from utils.get_data import get_test_data
import logging
case,par_to_test = get_test_data(get_data_path(__file__))
 
 
class TestFixture3(object):
    """
    """
 
    def test_fixture_3(self,param,env):
        url= env["host"]["local"]+env["APIS"]["add_message"]
        response = requests.request("POST", url, data=param[3], headers=param[1])
        res = response.json()
        print(res)

测试数据:

{
 
    "test": [
      {
        "case": "这是第一个测试用例",
        "headers": {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        "querystring": {
        },
        "payload": {
                "mid" :"115",
                "name" :"android9",
                "content" : "8" ,
                "status": "1",
                "author" :"xixi"
        },
        "expected":
          {
}
      },
 
      {
        "case": "这是第2个测试用例",
        "headers": {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        "querystring": {
        },
        "payload": {
                "mid" :"115",
                "name" :"android9",
                "content" : "8" ,
                "status": "1",
                "author" :"xixi"
        },
        "expected":
          {
}
      },
 
      {
        "case": "这是第3个测试用例",
        "headers": {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        "querystring": {
        },
        "payload": {
                "mid" :"115",
                "name" :"android9",
                "content" : "8" ,
                "status": "1",
                "author" :"xixi"
        },
        "expected":
          {
}
      }
 
 
    ]
}

获取测试数据的代码:

def get_data_path(case_path):
    file_name = os.path.dirname(case_path).split(os.sep + 'tests' + os.sep, 1)
    test_data = os.sep.join([file_name[0], 'data', file_name[1], os.path.basename(case_path).replace('.py', '.json')])
    return test_data
 
def get_test_data(test_data_path):
    case = []
    headers = []
    querystring = []
    payload = []
    expected = []
    with open(test_data_path,encoding='utf-8') as f:
        dat = json.loads(f.read())
        test = dat['test']
        for td in test:
            case.append(td['case'])
            headers.append(td.get('headers', {}))
            querystring.append(td.get('querystring', {}))
            payload.append(td.get('payload', {}))
            expected.append(td.get('expected', {}))
    list_parameters = list(zip(case, headers, querystring, payload, expected))
    return case,list_parameters

五.Conclusion

pytest通过这种plugin的方式,大大增强了这个测试框架的实用性,可以看到pytest本身的许多组件也是通过plugin的方式加载的,可以说pytest就是由许许多多个plugin组成的。另外,通过定义好一些hook spec,可以有效地控制plugin的“权限”,再通过类似pytest.hookimpl这样的装饰器又可以增强了各种plugin的“权限”。这种design对于pytest这样复杂的框架而言无疑是非常重要的,这可能也是pytest相比于其他测试框架中越来越?的原因吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于学习 pytest 测试框架,我可以给您一些简要的介绍和指导。 首先,pytest 是一个功能强大且易于使用的 Python 测试框架,它可以用于编写和执行各种类型的测试,包括单元测试、集成测试和功能测试等。 以下是一些学习 pytest 的步骤和资源: 1. 安装 pytest:您可以使用 pip 命令来安装 pytest。在命令行中运行以下命令即可: ``` pip install pytest ``` 2. 学习基本语法:pytest 提供了丰富的语法和功能来编写测试用例。您可以开始学习 pytest 的基本语法,例如使用 `assert` 来编写断言、使用 `test_` 前缀定义测试函数等。 3. 编写测试用例:在 pytest 中,测试用例是以函数的形式存在的。您可以创建一个 Python 模块,并在其中定义一个或多个测试函数。每个测试函数应该以 `test_` 开头,这样 pytest 才能自动识别并执行它们。 4. 运行测试:使用命令行进入到测试用例所在的目录,并执行以下命令来运行测试: ``` pytest ``` pytest 会自动发现并执行所有的测试用例,并输出测试结果。 5. 断言和测试覆盖率:pytest 提供了多种断言方法,您可以根据需要选择适合的断言来验证测试结果。此外,还可以使用 pytest-cov 插件来获取测试覆盖率报告,以了解您的测试用例覆盖了哪些代码。 6. 学习更高级的功能:pytest 还提供了许多高级功能,例如参数化测试、夹具(fixtures)、插件系统等。您可以逐步学习和掌握这些功能,以便更好地利用 pytest 进行测试。 除了以上步骤和资源,还有许多书籍、教程和文档可以帮助您深入学习 pytest。以下是一些值得参考的资源: - pytest 官方文档:https://docs.pytest.org/ - "Python Testing with pytest" 一书:https://pragprog.com/titles/bopytest/python-testing-with-pytest/ - pytest 教程:https://www.tutorialspoint.com/pytest/index.htm 希望这些信息能对您学习 pytest 有所帮助!如有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值