pytest文档69-Hook函数之参数化生成测试用例pytest_generate_tests

前言

pytest 实现参数化有三种方式

  • pytest.fixture() 使用 fixture 传 params 参数实现参数化
  • @ pytest.mark.parametrize 允许在测试函数或类中定义多组参数,在用例中实现参数化
  • pytest_generate_tests 允许定义自定义参数化方案或扩展。

pytest_generate_tests

pytest_generate_tests 在测试用例参数化收集前调用此钩子函数,根据测试配置或定义测试函数的类或模块中指定的参数值生成测试用例,
可以使用此钩子实现自定义参数化方案或扩展,相关文档参考官方文档https://docs.pytest.org/en/latest/parametrize.html#pytest-generate-tests

有时您可能想要实现自己的参数化方案或实现某种动态性来确定 fixture 的参数或范围。为此,可以使用pytest_generate_tests在收集测试函数时调用的钩子。通过传入的 metafunc 对象,您可以检查请求的测试上下文,最重要的是,您可以调用 metafunc.parametrize() 引起参数化。

例如,假设我们要运行一个测试,并接受要通过新的 pytest 命令行选项设置的字符串输入。让我们首先编写一个接受 stringinput 函数参数的简单测试:

# content of test_strings.py


def test_valid_string(stringinput):
    assert stringinput.isalpha()

现在,我们添加一个conftest.py文件,其中包含命令行选项和测试函数的参数化:

# content of conftest.py


def pytest_addoption(parser):
    parser.addoption(
        "--stringinput",
        action="append",
        default=[],
        help="list of stringinputs to pass to test functions",
    )


def pytest_generate_tests(metafunc):
    if "stringinput" in metafunc.fixturenames:
        metafunc.parametrize("stringinput", metafunc.config.getoption("stringinput"))

如果现在传递两个stringinput值,则测试将运行两次:

$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
..                                                                   [100%]
2 passed in 0.12s

我们还使用一个stringinput运行,这将导致测试失败:

$ pytest -q --stringinput="!" test_strings.py
F                                                                    [100%]
================================= FAILURES =================================
___________________________ test_valid_string[!] ___________________________

stringinput = '!'

    def test_valid_string(stringinput):
>       assert stringinput.isalpha()
E       AssertionError: assert False
E        +  where False = <built-in method isalpha of str object at 0xdeadbeef>()
E        +    where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha

test_strings.py:4: AssertionError
========================= short test summary info ==========================
FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False
1 failed in 0.12s

不出所料,我们的测试功能失败。

如果您未指定字符串输入,则将跳过它,因为 metafunc.parametrize()将使用空参数列表来调用它:

$ pytest -q -rs test_strings.py
s                                                                    [100%]
========================= short test summary info ==========================
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:2
1 skipped in 0.12s

请注意,当metafunc.parametrize使用不同的参数集多次调用时,这些集合中的所有参数名称都不能重复,否则会引发错误。
更多的参数化案例参考https://docs.pytest.org/en/latest/example/parametrize.html#paramexamples

使用示例

在 conftest.py 自定义参数化的钩子, 判断当测试用例传了 param 参数,就让它生成参数化的用例

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

test_parm.py相关的测试数据和用例内容,names是用例的名称,test_data是测试数据

import requests

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

# 用例的id名称
names = ["login nam1", "login name2"]
# 测试数据 list of dict
test_data = [{
                "url": "http://49.235.x.x:5000/api/v1/login/",
                "method": "POST",
                "headers":
                    {
                        "Content-Type": "application/json"
                    },
                "json":
                    {
                        "username": "test",
                        "password": "123456"
                    }

        },
        {
            "url": "http://49.235.x.x:5000/api/v1/login/",
            "method": "POST",
            "headers":
                {
                    "Content-Type": "application/json"
                },
            "json":
                {
                    "username": "test",
                    "password": "123456"
                }

        }
        ]


def test_login(param):
    r = requests.session().request(**param)
    print(r.text)

这样运行会,自动生成2条测试用例

================= test session starts ===============
platform win32 -- Python 3.6.6, pytest-4.5.0, py-1.9.0, pluggy-0.13.1
rootdir: D:\
collected 2 items

..\test_param.py 
{"code": 0, "msg": "login success!", "username": "test", "token": "81ae10099feaca6f0af5ba122444bea2a83a2dc9"}
.{"code": 0, "msg": "login success!", "username": "test", "token": "1ccfb99fd68f22da66c95660702af22d64108067"}
.

=============== 2 passed in 0.62 seconds ===============

上面是把测试数据和代码放一起,想实现代码和数据分离的话,上面的 names 和 test_data 测试数据写到 yaml 文件,写个函数去读取 yaml 文件的数据。

网易云完整视频课程《pytest+yaml 框架使用与开发》https://study.163.com/course/courseMain.htm?courseId=1213419817&share=2&shareId=480000002230338

posted @ 2020-12-09 09:45  上海-悠悠  阅读(3434)  评论(2编辑  收藏  举报