zoukankan      html  css  js  c++  java
  • 【pytest官方文档】解读-fixtures函数和测试函数的参数化

    Pytest会在以下几个级别启用测试参数化:

    • pytest.fixture(),可以对fixture函数进行参数化。
    • @pytest.mark.parametrize,可以在测试函数或类中定义多组参数和fixture。
    • pytest_generate_tests,可以自定义参数化方案或扩展。

    一、@pytest.mark.parametrize:参数化测试函数

    1. 常规用法

    对测试函数的参数进行参数化,直接使用内置的装饰器pytest.mark.parameterized即可。

    import pytest
    
    
    @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
    def test_eval(test_input, expected):
        assert eval(test_input) == expected
    

    从代码里可以看出,在装饰器里定义了三个不同的元组。我们把("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
    拆开看:

    • "test_input,expected":这个字符串里定义了2个参数,test_inputexpected
    • ("3+5", 8), ("2+4", 6), ("6*9", 42):这里3个元组,没个元组里有2个元素,依次序分别对应test_inputexpected
    • 3个元组外层的[]:列表里就是参数化具体的传参了,因为里面传了3个不同的元组,所以测试函数test_eval会分别执行3次。
    ============================= test session starts =============================
    platform win32 -- Python 3.9.4, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
    rootdir: D:PycharmProjectswms-apiinterface, configfile: pytest.inicollected 3 items
    
    test_module1.py ..F
    demo	est_module1.py:3 (test_eval[6*9-42])
    54 != 42
    
    Expected :42
    Actual   :54
     <Click to see difference>
    
    test_input = '6*9', expected = 42
    
        @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
        def test_eval(test_input, expected):
    >       assert eval(test_input) == expected
    E       AssertionError: assert 54 == 42
    E        +  where 54 = eval('6*9')
    
    test_module1.py:6: AssertionError
    

    运行结果可以看到最后一次失败了,因为第三次运行测试函数取的参数是 ("6*9", 42),54不等于42,所以断言失败。

    2. 在参数化中标记单个测试实例

    在参数化中标记单个测试实例,比如之前提到过的mark.xfail,这个可以标记测试函数为失败。那么在参数化中,如果想让其中的某个参数运行
    的时候测试失败,就可以这样用:

    import pytest
    
    
    @pytest.mark.parametrize(
        "test_input,expected",
        [("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
    )
    def test_eval(test_input, expected):
        assert eval(test_input) == expected
    

    运行一下:

    test_module1.py                                                       [100%]
    
    ======================== 2 passed, 1 xfailed in 0.05s =========================..x
    

    3. 多个参数化组合,笛卡尔积

    如果在测试函数上加了多个参数化装饰器,那么得到的参数组合是一个笛卡尔积:

    import pytest
    
    
    @pytest.mark.parametrize("x", [0, 1])
    @pytest.mark.parametrize("y", [2, 3])
    def test_foo(x, y):
        print("
    x:", x)
        print("y:", y)
    

    应该会组合成4组数据x=0/y=2, x=1/y=2, x=0/y=3, 和x=1/y=3,测试函数执行4次:

    test_module1.py .
    x: 0
    y: 2
    .
    x: 1
    y: 2
    .
    x: 0
    y: 3
    .
    x: 1
    y: 3
                                                         [100%]
    
    ============================== 4 passed in 0.01s ==============================
    

    二、用钩子函数pytest_generate_tests example拓展

    如果有些场景需要动态的确定参数或者fixture的使用范围,那么可以使用pytest_generate_tests这个钩子函数,该函数会在收集测试函数时候被调用。
    通过传入的metafunc对象,可以检查请求测试函数的上下文,还可以进一步的调用metafunc.parameterize()来实现参数化。

    举例,有个测试函数需要接受输入的字符串作为参数,而且通过pytest命令行获取到,那么就要编写一个获取参数的fixture函数来给测试函数调用。

    # content of test_strings.py
    
    def test_valid_string(stringinput):
        assert stringinput.isalpha()
    

    新建conftest.py文件,fixture函数写在这里:

    # 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"))
    

    现在用命令行方式来运行这个测试函数:pytest -q --stringinput="hello" --stringinput="world" test_strings.py,会运行2次。

    D:PycharmProjectswms-apiinterfacedemo>pytest -q --stringinput="hello" --stringinput="world" test_strings.py
    ..                                                                                                                                                                     [100%]
    2 passed in 0.01s
    

    再换个输入参数,让测试函数失败:pytest -q --stringinput="!" test_strings.py

    FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False
    1 failed in 0.04s
    

    如果没有字符串输入,那么测试函数它将被跳过。因为metafunc.parameterize()被调用时,传过去的是一个列表:pytest -q -rs test_strings.py

    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时, 如果使用不同的参数集进行多次参数化,这些参数集上的所有参数名称都不能重复,否则将会报错。

    总结:文中讲到的3种用法,实际应用中第一种最常见,第二种次之,至于第三种,可以作为一些用法启发。

    --不要用肉体的勤奋,去掩盖思考的懒惰--
  • 相关阅读:
    Springboot 之 自定义配置文件及读取配置文件
    SQLSERVER系统视图 sql server系统表详细说明
    MySQL Workbench建表时 PK NN UQ BIN UN ZF AI 的含义
    使用Ecplise git commit时出现"There are no stages files"
    maven添加sqlserver的jdbc驱动包
    java将XML文档转换成json格式数据
    java将XML文档转换成json格式数据
    cannot be resolved. It is indirectly referenced from required .class files
    org.codehaus.jackson.map.JsonMappingException: Can not construct instance of java.util.Date from String value '2012-12-12 12:01:01': not a valid representation (error: Can not parse date "2012-12-
    @Autowired注解和静态方法 NoClassDefFoundError could not initialize class 静态类
  • 原文地址:https://www.cnblogs.com/pingguo-softwaretesting/p/14725270.html
Copyright © 2011-2022 走看看