Python-pytest一些高级功能

文章目录

  • 一、fixtures
      • 1. 参数详情
      • 2. fixture的三种调用方式
  • 二、参数化测试
  • 三、 hook钩子
  • 四、插件和扩展
      • 1. pytest-repeat 重复跑
      • 2. pytest-assume 断言后继续跑
      • 3. pytest-ordering 用例顺序
      • 4.pytest-dependency 用例依赖
      • 5.pytest-rerunfailures 用例失败重跑
      • 6.pytest-xdist 分布式执行
      • 7.pytest-xfail 预期失败


一、fixtures

Fixtures 是 pytest 中一种强大的功能,主要用于设置测试前的预条件,以及在测试后进行清理。

1. 参数详情

@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None)
  • scope:用于控制Fixture的作用范围,
    默认取值为function(函数级别),控制范围的排序为:session > module > class > function
    function:每个测试函数都会得到一个新的 fixture 实例(默认作用域)。
    class:每个测试类只实例化一次 fixture,对类中的所有测试方法共享。
    module:每个模块只实例化一次 fixture,对模块中的所有测试函数共享。
    session:每个测试会话只实例化一次 fixture,对所有测试共享。

  • params:参数允许您为 fixture 指定一组参数,pytest 将为这些参数的每个元素生成一次测试。
    在这个例子中,test_param_fixture 将针对每个 params 中的值运行一次。

@pytest.fixture(params=[1, 2, 3])
def param_fixture(request):
    return request.param

def test_param_fixture(param_fixture):
    assert param_fixture in [1, 2, 3]
  • autouse:为True的话就是开启,开启后范围的方法都会自动去执行。默认为False。
  • ids:字符串id的列表,每个id对应于参数,是测试id的一部分,如果没有提供id,它们将从参数自动生成标识。
  • name:可以理解成为前置函数取个别名。

2. fixture的三种调用方式

  1. 最基本的使用方法是将 fixture 直接作为测试函数的参数。这种情况下,pytest 在调用测试函数时会自动处理 fixture。
@pytest.fixture
def sample_fixture():
    return "some data"

def test_example(sample_fixture):
    assert sample_fixture == "some data"
  1. 使用 usefixtures 装饰器
    如果不需要直接访问 fixture 返回的值,但仍然需要执行 fixture(例如设置环境),可以使用 @pytest.mark.usefixtures 装饰器。
@pytest.fixture
def setup_environment():
    # 设置环境
    print("Setting up environment")
    yield
    # 清理环境
    print("Tearing down environment")

@pytest.mark.usefixtures("setup_environment")
def test_environment():
    # 这里不需要直接使用 setup_environment 返回的任何值
    assert True
  1. 作为类的属性
    如果在测试类中使用 fixture,并且希望在多个测试方法中共享相同的 fixture 实例,可以将 fixture 作为类的一个属性。
@pytest.fixture(scope="class")
def class_fixture():
    return "class data"

@pytest.mark.usefixtures("class_fixture")
class TestClass:
    def test_one(self):
        assert self.class_fixture == "class data"

    def test_two(self):
        assert self.class_fixture == "class data"
  1. 使用 conftest.py
    可以在 conftest.py 文件中定义通用的 fixture,这样它们就可以在多个测试文件中使用,而无需显式导入。
# conftest.py
@pytest.fixture
def global_fixture():
    return "global data"

# test_example.py
def test_global(global_fixture):
    assert global_fixture == "global data"
  1. Fixture 的参数化
    可以参数化 fixture 本身,通过 params 属性为 fixture 提供不同的输入值。
@pytest.fixture(params=[1, 2, 3])
def param_fixture(request):
    return request.param

def test_param_fixture(param_fixture):
    assert param_fixture in [1, 2, 3]
  1. 依赖于其他 fixtures
    Fixture 可以使用其他 fixtures 作为它们的输入。
@pytest.fixture
def base_fixture():
    return "base data"

@pytest.fixture
def dependent_fixture(base_fixture):
    return f"dependent on {base_fixture}"

def test_dependent_fixture(dependent_fixture):
    assert dependent_fixture == "dependent on base data"
  1. 使用参数autouse调用fixture
    当设置了 autouse=True,该 fixture 将自动应用于同一作用域中的所有测试,这对于需要在许多测试中共用的初始化和清理任务特别有用。
  • 对单个测试函数自动使用 fixture:
@pytest.fixture(autouse=True)
def setup_and_teardown():
    # 这里可以进行测试前的设置
    print("Setting up for a test")
    yield
    # 这里可以进行测试后的清理
    print("Tearing down after a test")

def test_example():
    assert True  # 这个测试会自动使用 setup_and_teardown fixture
  • 对测试类中的所有方法自动使用 fixture:
@pytest.fixture(autouse=True)
def setup_and_teardown():
    print("Setting up")
    yield
    print("Tearing down")

class TestClass:
    def test_one(self):
        assert True  # 这里会自动使用 setup_and_teardown fixture

    def test_two(self):
        assert True  # 这里也会自动使用 setup_and_teardown fixture

在这个例子中,setup_and_teardown fixture 将自动应用于 TestClass 类中的所有测试方法。

  • 使用 conftest.py 中的全局 autouse fixture:
    在 conftest.py 文件中定义的带有 autouse=True 的 fixture 将在对应作用域的所有测试中自动使用,无需显式引用。
# conftest.py
@pytest.fixture(autouse=True)
def global_setup():
    print("Global setup for all tests")
    yield
    print("Global teardown after all tests")

# test_example.py
def test_something():
    assert True  # 这里会自动使用 global_setup fixture

在这种情况下,global_setup fixture 将在所有测试开始前自动执行,无论它们定义在哪个文件中。

二、参数化测试

pytest.mark.parametrize: 装饰器允许您为单个测试函数提供多组参数和值,以便进行多次独立测试。

@pytest.mark.parametrize("x, y, expected", [(1, 1, 2), (2, 3, 5)])
def test_add(x, y, expected):
    assert x + y == expected

pytest.mark.parametrize 读取yml文件

@pytest.mark.parametrize('args', yaml.safe_load(open(dataPath, encoding='utf-8'))['instances_test'])

三、 hook钩子

  1. 配置和初始化

pytest_configure(config):在命令行参数解析之后,测试运行开始之前调用。
pytest_addoption(parser):添加自定义命令行选项。

  1. 收集测试用例

pytest_collection_modifyitems(session, config, items):在测试用例收集完成后调用,可以用来修改收集到的测试项。
pytest_itemcollected(item):当一个测试项被收集时调用。

  1. 设置和清理

pytest_fixture_setup(fixturedef, request):在执行 fixture 前调用。
pytest_fixture_post_finalizer(fixturedef, request):在 fixture 被清理后调用。

  1. 测试执行

pytest_runtest_protocol(item, nextitem):自定义测试执行的协议。
pytest_runtest_setup(item):在每个测试项的 setup 阶段调用。
pytest_runtest_call(item):在调用测试函数时调用。
pytest_runtest_teardown(item, nextitem):在每个测试项的 teardown 阶段调用。
pytest_runtest_logreport(report):在生成测试报告后调用。

  1. 异常处理

pytest_exception_interact(node, call, report):在测试中出现异常时调用。

  1. 报告和日志

pytest_terminal_summary(terminalreporter, exitstatus, config):在测试运行结束时,生成终端总结报告。
pytest_report_header(config, startdir):添加额外的头部信息到测试报告中。
pytest_report_teststatus(report, config):自定义测试的状态报告(如 pass, fail, skip)。

  1. 会话开始和结束

pytest_sessionstart(session):在会话开始时调用。
pytest_sessionfinish(session, exitstatus):在会话结束时调用。

四、插件和扩展

1. pytest-repeat 重复跑

第一种用法: 装饰器 @pytest.mark.repeat(次数)

@pytest.mark.repeat(5)

第二种用法: 命令行参数

pytest --count=5 test.py

第三种用法: 结合repeat-scope运行

pytest.main(['-sv','--count=2','--repeat-scope=session',__file__])

2. pytest-assume 断言后继续跑

在测试代码中,您可以使用 pytest.assume() 替代标准的 assert 语句。这样,即使某个假设失败,测试也会继续执行,直到所有的假设都被检查完毕。
import pytest

def test_multiple_assumptions():
    pytest.assume(1 == 2, "One is not equal to Two")
    pytest.assume(True, "This assumption is True")
    pytest.assume(3 == 3, "Three is equal to Three")

测试结果会显示所有断言的状态。失败的假设会被标记出来,但它们不会阻止测试的其他部分执行。

3. pytest-ordering 用例顺序

  • pytest默认按字母顺序去执行的(小写英文—>大写英文—>0-9数字)
  • 用例之间的顺序是文件之间按照ASCLL码排序,文件内的用例按照从上往下执行。
  • 改变测试用例的执行顺序,用法是加上装饰器
@pytest.mark.run(order=[number])

4.pytest-dependency 用例依赖

主要解决用例之间的依赖关系。如果依赖的上下文失败后续的用例会被标识为跳过执行,相当于执行了 pytest.mark.skip

需要使用装饰器来标记测试用例的依赖关系。

import pytest

@pytest.mark.dependency()
def test_a():
    assert True

@pytest.mark.dependency(depends=["test_a"])
def test_b():
    pass

在上述例子中,test_b 依赖于 test_a 的成功执行。如果 test_a 失败,test_b 将被自动跳过。

使用分组来管理依赖

import pytest

@pytest.mark.dependency(name="group1")
def test_c():
    assert True

@pytest.mark.dependency(name="group2")
def test_d():
    assert True

@pytest.mark.dependency(depends=["group1", "group2"])
def test_e():
    pass

在这个例子中,test_e 依赖于 test_c 和 test_d 的成功。如果任何一个测试失败,test_e 将被跳过。

5.pytest-rerunfailures 用例失败重跑

使用方法一: 装饰器

@pytest.mark.flaky(reruns=50,reruns_delay=2) #重跑50次,每次间隔2s

使用方法二: 命令行

pytest.main(['-sv','--reruns=2','--reruns-delay=2',__file__])

6.pytest-xdist 分布式执行

它允许你以分布式方式并行运行测试。这意味着你可以在多个CPU核心上同时运行测试,或者在不同的机器上分布测试,从而显著减少测试运行的总时间。

并行在多个CPU核心上运行

pytest -n 4

7.pytest-xfail 预期失败

知道某个测试由于某些原因(如尚未修复的 bug、未实现的功能或依赖于外部系统的测试)将会失败,但您仍希望包含此测试在测试套件中时,可以使用 pytest.mark.xfail。

import pytest

@pytest.mark.xfail
def test_example():
    assert False

在这个例子中,test_example 测试预期会失败。

import pytest
import sys

@pytest.mark.xfail(sys.version_info < (3,8), reason="requires python3.8 or higher")
def test_example():
    assert True

在这个例子中,只有当 Python 版本低于 3.8 时,test_example 测试才预期失败。

访问 xfail 的结果
您可以在测试报告中看到哪些测试被标记为失败,以及它们的原因。

你可能感兴趣的:(Python,python,pytest,chrome)