zoukankan      html  css  js  c++  java
  • 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
    
    ..	est_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 文件的数据。

  • 相关阅读:
    Mybatis详解(二)
    Mybatis详解(一)
    Java集合
    Java基础之IO
    Java异常知识点!
    HTTP状态码
    ajax传字符串时出现乱码问题的解决
    Json 文件 : 出现 Expected value at 1:0 问题的解决
    java @XmlTransient与@Transient区别
    文件的上传和回显
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/14106959.html
Copyright © 2011-2022 走看看