测试学习之——Pytest Day4

发布于:2025-07-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

Pytest作为Python中功能强大且易于使用的测试框架,深受开发者喜爱。它不仅提供了简洁的测试编写方式,还通过丰富的配置选项、灵活的标记机制和强大的数据驱动能力,极大地提升了测试效率和可维护性。本文将深入探讨Pytest的配置意义与层级、常用命令参数、标记(mark)的使用,以及数据驱动测试的实现方法。

一、Pytest配置:意义、层级与常用参数

Pytest的配置允许我们自定义测试行为,例如指定测试文件发现规则、添加命令行选项、定义fixture等。理解其配置意义和层级对于高效使用Pytest至关重要。

1. 配置意义与层级

Pytest的配置具有层级性,允许在不同粒度上进行设置,从项目级别到测试文件级别。当存在多个配置时,Pytest会按照一定的优先级进行合并和覆盖。

•全局配置: 通常通过pytest.ini、pyproject.toml或setup.cfg文件在项目根目录进行配置。这些配置对整个项目的所有测试都有效。

•目录级配置: 可以在子目录中放置pytest.ini等配置文件,这些配置仅对当前目录及其子目录下的测试文件生效,并会覆盖全局配置中的同名设置。

•模块/文件级配置: 在测试文件中,可以通过pytest.mark等装饰器对单个测试函数或类进行更细粒度的配置。

•命令行参数: 命令行参数具有最高的优先级,会覆盖所有文件中的配置。

2. 常用命令参数

Pytest提供了丰富的命令行参数,用于控制测试的执行行为。以下是一些常用的参数:

•-v:显示更详细的测试结果(verbose)。

•-s:允许捕获标准输出(stdout)和标准错误(stderr),通常用于打印调试信息。

•-x:遇到第一个失败的测试时立即停止测试。

•--lf (--last-failed):只运行上次失败的测试。

•--ff (--failed-first):先运行上次失败的测试,然后运行所有其他测试。

•-k <expression>:根据名称表达式选择要运行的测试。例如,-k "keyword and not another_keyword"。

•-m <marker_expression>:根据标记表达式选择要运行的测试。例如,-m "web and not slow"。

•--collect-only:只收集测试,不执行。

•--maxfail=<num>:在达到指定数量的失败后停止测试。

•--html=<path>:生成HTML格式的测试报告(需要安装pytest-html插件)。

•--alluredir=<path>:指定Allure报告结果的输出目录(需要安装allure-pytest插件)。

示例:命令行执行

pytest -v -s my_test_file.py pytest -m "smoke" pytest --alluredir=./allure-results

3. 配置文件:pytest.ini

pytest.ini是Pytest最常用的配置文件,它允许我们将常用的命令行参数、标记注册、测试发现规则等固化到文件中,避免每次都在命令行中输入。一个典型的pytest.ini文件可能包含以下内容:

# pytest.ini
[pytest]
# 添加默认的命令行参数
addopts = -vs --strict-markers

# 注册自定义标记,避免出现Unknown marker警告
markers =
    smoke: 冒烟测试
    regression: 回归测试
    web: Web自动化测试

# 配置测试文件和目录的发现规则
python_files = test_*.py *_test.py
python_classes = Test*
python_functions = test_

# 配置测试路径,只在指定路径下查找测试
testpaths =
    tests/
    e2e_tests/

# 配置allure报告输出目录(如果使用allure)
# addopts = --alluredir=./allure-results --clean-alluredir




配置说明:

•addopts:用于设置默认的命令行参数,每次运行pytest时都会自动加上这些参数。

•markers:用于注册自定义的测试标记。注册后,Pytest就不会对这些标记发出“Unknown marker”警告。

•python_files、python_classes、python_functions:定义Pytest如何发现测试文件、测试类和测试函数。

•testpaths:指定Pytest应该在哪些目录下查找测试文件。

二、标记(Mark):用户自定义与框架内置

Pytest的标记(pytest.mark)是一个强大的功能,它允许我们对测试函数、测试类甚至模块进行分类和筛选。通过标记,我们可以灵活地选择性执行特定类型的测试,例如只运行冒烟测试、跳过某些测试等。

1. 用户自定义标记步骤

自定义标记的步骤非常简单:

1.在测试代码中使用@pytest.mark.<marker_name>装饰器: 将标记应用到测试函数或类上。

2.在pytest.ini中注册标记(可选但推荐): 在pytest.ini文件的[pytest]部分添加markers选项,注册自定义标记。这可以避免Pytest在运行时发出“Unknown marker”警告,并提供标记的描述。

3.通过命令行执行带有特定标记的测试: 使用-m参数加上标记表达式来运行测试。

示例:自定义标记

# test_example.py
import pytest

@pytest.mark.smoke
def test_login_success():
    assert True

@pytest.mark.regression
@pytest.mark.slow
def test_complex_calculation():
    assert True

@pytest.mark.web
def test_homepage_load():
    assert True

pytest.ini配置:

# pytest.ini
[pytest]
markers =
    smoke: 冒烟测试
    regression: 回归测试
    slow: 运行缓慢的测试
    web: Web自动化测试

 Web自动化测试

执行命令:

# 运行所有冒烟测试
pytest -m smoke

# 运行所有回归测试,但不包括慢速测试
pytest -m "regression and not slow"

# 运行所有web测试
pytest -m web

2. 框架内置标记

Pytest也提供了一些内置的标记,用于处理常见的测试场景:

•@pytest.mark.skip:无条件跳过测试。可以提供一个reason参数说明跳过的原因。

•@pytest.mark.skipif(condition, reason):根据条件跳过测试。当condition为True时跳过。

•@pytest.mark.xfail(condition, reason, raises):预期失败的测试。即使测试失败,也不会计入失败总数,而是标记为“xfailed”。

•@pytest.mark.parametrize(argnames, argvalues):用于数据驱动测试,将在下一节详细介绍。

三、Pytest的数据驱动测试:parametrize实现

数据驱动测试(Data-Driven Testing)是一种测试方法,它将测试逻辑与测试数据分离。通过使用不同的测试数据重复执行相同的测试逻辑,可以有效地增加测试覆盖率并减少代码冗余。Pytest通过@pytest.mark.parametrize装饰器提供了强大的数据驱动能力。

1. parametrize的基本用法

@pytest.mark.parametrize装饰器接受两个主要参数:

•argnames:一个字符串,包含用逗号分隔的参数名称。这些参数将作为测试函数的参数传入。

•argvalues:一个列表,包含参数值的元组或列表。每个元组/列表对应一次测试执行的参数组合。

示例:单个参数的数据驱动

# test_data_driven.py
import pytest

@pytest.mark.parametrize("input_value, expected_output", [
    (1, 2),
    (2, 3),
    (3, 4),
])
def test_increment(input_value, expected_output):
    assert input_value + 1 == expected_output

在这个例子中,test_increment函数会被执行三次,每次传入不同的input_value和expected_output。

2. 多个参数的数据驱动

parametrize可以很方便地处理多个参数的组合。

示例:多个参数的数据驱动

# test_data_driven.py
import pytest

@pytest.mark.parametrize("num1, num2, expected_sum", [
    (1, 2, 3),
    (0, 0, 0),
    (-1, 5, 4),
    (10, -3, 7),
])
def test_addition(num1, num2, expected_sum):
    assert num1 + num2 == expected_sum

3. parametrize与fixture结合

parametrize还可以与fixture结合使用,为每个参数组合提供不同的fixture实例。

示例:parametrize与fixture结合

# conftest.py
import pytest

@pytest.fixture
def setup_data(request):
    # request.param 会获取到parametrize传入的参数
    data = request.param * 2
    yield data

# test_data_driven.py
import pytest

@pytest.mark.parametrize("setup_data", [1, 2, 3], indirect=True)
def test_with_fixture_data(setup_data):
    # setup_data 会依次是 2, 4, 6
    assert setup_data % 2 == 0

在@pytest.mark.parametrize中设置indirect=True,表示setup_data不是直接作为参数传入,而是作为fixture的名称,Pytest会为每个参数值调用setup_data fixture。

4. 从外部文件加载数据

对于大量的测试数据,通常会将其存储在外部文件(如CSV、JSON、YAML)中,然后动态加载。这里以YAML为例,结合之前提到的PyYAML库。

data.yaml文件:

# data.yaml
- test_case:
    id: 1
    input: 10
    expected: 20
- test_case:
    id: 2
    input: 0
    expected: 0
- test_case:
    id: 3
    input: -5
    expected: -10

test_from_file.py文件:

import pytest
import yaml

def load_test_data(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        return yaml.safe_load(f)

@pytest.mark.parametrize("test_data", load_test_data('data.yaml'))
def test_multiply_by_two(test_data):
    input_value = test_data['test_case']['input']
    expected_output = test_data['test_case']['expected']
    assert input_value * 2 == expected_output

这种方式使得测试数据与测试逻辑完全分离,便于数据的管理和维护。

总结

Pytest通过其灵活的配置机制、强大的标记功能和便捷的数据驱动能力,为Python项目的测试提供了全面的支持。合理利用pytest.ini进行项目级配置,通过@pytest.mark对测试进行分类和筛选,以及运用@pytest.mark.parametrize实现数据驱动测试,都能够显著提升测试的效率、可读性和可维护性。掌握这些进阶用法,将帮助您构建更加健壮和高效的自动化测试体系。


网站公告

今日签到

点亮在社区的每一天
去签到