读后笔记 -- Pytest框架与自动化应用 Chapter4:DDT 和 参数传递

4.2 参数化应用

1. 单一参数化 / 多参数化

# content of test_mark_parametrize.py
mport pytest

@pytest.mark.parametrize("test_case", [1, 2, 3, 'orange', 'apple'])
def test_string(test_case):  # 单一参数化
    print(f"\n我们的测试数据:{test_case}")

@pytest.mark.parametrize("test_input, expected", [("3 + 5", 8),
                                                  ("2 + 5", 7),
                                                  ("7 * 5", 30),
                                                  ])
def test_eval(test_input, expected):    # 多参数化
    # eval将字符串str当成有效的表达式来求值并返回计算结果
    assert eval(test_input) == expected

2. 多个参数化

# content of test_multi.py
"""多个参数化,将会变成组合的形式,示例将产生 3*2 = 6 个组合"""
import pytest

@pytest.mark.parametrize('test_input', [1, 2, 3])
@pytest.mark.parametrize('test_output, expected', [(1, 2), (3, 4)])
def test_multi(test_input, test_output, expected):
    pass

3. pytestmark 实现参数化

# content of test_module.py
import pytest

# 通过对 pytestmark 赋值,参数化一个测试模块
pytestmark = pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)])

def test_module(test_input, expected):
    assert test_input + 1 == expected

 


4.4 argnames 参数

1. argnames 与测试方法中的参数关系

# 1) 测试方法未声明, mark.parametrize 中声明
# if run this only, report "function uses no argument 'expected'
@pytest.mark.parametrize('input, expected', [(1, 2)])
def test_sample1(input):
    assert input + 1 == 1
# 2) 测试方法参数声明的范围小于 mark.parametrize 中声明的范围:parametrize 定义了 expected,又在方法入参中赋值 expected,将出错
# if run this only, report "function already takes an argument 'expected' with a default value"
@pytest.mark.parametrize('input, expected', [(1, 2)])
def test_sample2(input, expected = 2):
    assert input + 1 == 1
# content of test_mark_param_sub.py
import pytest

@pytest.fixture
def expected3():
    return 2

# 3) test_sample3 没有定义 expected,test_sample3 的参数 expected3 可以从 fixture 中获取
@pytest.mark.parametrize('input3', [(1)])
def test_sample3(input3, expected3):
    assert input3 + 1 == expected3

@pytest.fixture
def expected4():
    return 2

# 4)parametrize() 的参数化值覆盖了 fixture 的值
@pytest.mark.parametrize('input4, expected4', [(1, 2), [2, 3], set([3, 4])])
def test_sample4(input4, expected4):
    assert input4 + 1 == expected4

 


4.5 argvalues 参数

1. 使用 pytest.param 为 argvalues 赋值

# content of test_mark_param_sub.py
import pytest

@pytest.mark.parametrize(
    ('n', 'expected5'),
    [(4, 2),    # 正常传参 为 argvalues 赋值
     pytest.param(6, 3, marks=pytest.mark.xfail, id='XPASS')]    # 使用 pytest.parm() 为 argvalues 赋值
)
def test_param5(n, expected5):
    assert n / 2 == expected5

 


4.6 indirect 参数

# test_mark_param_indirect.py
"""
应用场景:同一个变量 在 fixture 定义和 参数化里定义
    当 indirect = False, var1 使用的是 参数化里面传的变量值
    当 indirect = True, var1 使用的是 fixture 的变量值
"""
import pytest

@pytest.fixture
def max_f(request):
    return request.param - 1

@pytest.fixture
def min_f(request):
    return request.param + 1

# 默认 indirect 为 False,min_f 和 max_f 使用的是后面的数据。
@pytest.mark.parametrize('min_f, max_f', [(1, 2), (3, 4)])
def test_indirect_default(min_f, max_f):
    assert min_f < max_f

# indirect 为 True时,min_f 和 max_f 对应的实参重定向到同名的 fixture 的数据
@pytest.mark.parametrize('min_f, max_f', [(1, 2), (3, 4)], indirect=True)
def test_indirect_true(min_f, max_f):
    assert min_f > max_f

# indirect = ['max_f'],仅 max_f 重定向到 fixture 的数据
@pytest.mark.parametrize('min_f, max_f', [(1, 2), (3, 4)], indirect=['max_f'])
def test_part_indirect(min_f, max_f):
    assert min_f == max_f

 


4.7 ids 参数

# content of test_mark_param_ids.py

import pytest

@pytest.mark.parametrize('input_1, expected', [(1, 2), (3, 4)],
                         ids=['first', 'second'])
def test_ids_with_ids1(input_1, expected):
    """ids 的长度 = argvalues 的长度"""
    pass

@pytest.mark.parametrize('input_2, expected', [(1, 2), (3, 4)],
                         ids=['num', 'num'])
def test_ids_with_ids2(input_2, expected):
    """ids 相同时,则显示 idxxx (索引),该示例显示 num0, num1"""
    pass

@pytest.mark.parametrize('input_3, expected', [(1, 2), (3, 4)],
                         ids=['num', '中文'])
def test_ids_with_ids4(input_3, expected):
    """在 pytest.ini 定义一个变量覆盖原始配置,将解决 ids 中文显示的问题"""
    pass

def idfn(val):
    return val + 1

@pytest.mark.parametrize('input_4, expected', [(1, 2), (3, 4)],
                         ids=idfn)
def test_ids_with_ids4(input_4, expected):
    """通过 idfn 将参数处理里一次
        默认显示:
            (1-2)
            (3-4)
        现在显示:
            (2-3)
            (4-5)
        """
    pass

@pytest.mark.parametrize('input_5, expected', [(1, 2), pytest.param(3, 4, id='id_via_pytest_param')],
                         ids=['first', 'second'])
def test_ids_with_ids5(input_5, expected):
    """pytest.param 指定的 id 将会覆盖 ids 对应的测试id
       结果将显示 [first] / [id_via_pytest_param]
    """
    pass

2. id 并可通过 "-k" 过滤

# content of test_id.py
import pytest

@pytest.mark.parametrize('input_1, expected', [
    pytest.param(1, 2, id='Windows'),
    pytest.param(3, 4, id='Windows'),
    pytest.param(5, 6, id='Non-Windows')
])
def test_ids_with_id(input_1, expected):
    pass

if __name__ == '__main__':
    """
     1. 使用下面的方式,不能过滤。
     2. 有效的方式:通过 terminal 执行 "pytest -k 'Non-Windows' .\test_ids.py"
    """
    pytest.main(['-k', 'Non-Windows', 'test-ids.py'])

 


4.8 scope 参数

# content of test_scope.py

import pytest

@pytest.fixture(scope='module')
def test_input(request):
    pass

@pytest.fixture(scope='module')
def expected(request):
    pass

@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)],
                         indirect=True)
def test_scope1(test_input, expected):
    pass

@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)],
                         indirect=True)
def test_scope2(test_input, expected):
    pass

@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)],
                         scope="module")
def test_scope3(test_input, expected):
    pass

@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)],
                         scope="module")
def test_scope4(test_input, expected):
    pass

@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)])
def test_scope5(test_input, expected):
    pass

@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4)])
def test_scope6(test_input, expected):
    pass

通过下面的输出,可以看出:

  • 1. 将先执行所有标识 scope= module第一组数据,再执行所有标识 scope= module第二组数据
  • 2. 未标识 scope=module 的,继续按原方式执行,一个方法组合执行完执行另一个方法的组合

 


4.9 pytest_generate_tests hook方法

1

2

 

posted on 2024-01-15 21:22  bruce_he  阅读(14)  评论(0编辑  收藏  举报