Python测试框架pytest从入门到实战:环境搭建、断言机制与高级功能详解
1. 项目概述为什么是pytest如果你正在写Python代码无论是Web后端、数据分析脚本还是桌面应用迟早会面临一个问题我怎么知道我的代码改对了而不是改坏了这就是测试的价值。在Python的测试世界里pytest早已不是“后起之秀”而是事实上的标准答案。它之所以能从早期的unittest、nose等框架中脱颖而出核心在于它彻底贯彻了“约定优于配置”和“让简单的事情保持简单复杂的事情成为可能”的理念。简单来说pytest是一个功能极其强大且高度灵活的测试框架。它让你可以用最符合直觉的Python语法来写测试——就是普通的assert语句不需要记忆一堆self.assertEqual()这样的方法名。同时它又通过插件系统和丰富的内置功能支撑起了从单元测试到复杂集成测试、从命令行小工具到大型企业级项目的全场景需求。你搜到的那些热词比如pytest allure报告、po模型和pytest框架、pytest接口自动化恰恰说明了它的生态有多么繁荣应用场景有多么广泛。这篇文章我就从一个写了多年Python测试的老兵视角带你从零开始彻底搞懂pytest的基础使用并分享那些官方文档里不会写的“实战心得”。2. 环境准备与第一个测试2.1 安装与验证安装pytest简单到令人发指。打开你的终端命令行一条命令搞定pip install pytest对于国内用户如果遇到网络问题可以使用清华、阿里云等镜像源加速pip install pytest -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后强烈建议验证一下版本因为不同版本间可能会有细微的语法或行为差异。执行pytest --version你会看到类似pytest 8.0.0的输出。我个人的习惯是在项目的requirements.txt或pyproject.toml中固定pytest的版本避免因版本升级导致CI/CD流水线或团队协作出现问题。例如在pyproject.toml的[project.optional-dependencies]或[tool.pytest.ini_options]部分可以注明测试依赖。注意如果你的项目同时使用了virtualenv、pipenv或poetry等虚拟环境/包管理工具请确保在正确的环境中安装。一个常见的坑是在系统Python中安装了pytest但实际项目运行在虚拟环境中导致运行测试时提示“pytest: command not found”。2.2 编写并运行第一个测试pytest的测试发现规则非常智能。默认情况下它会递归查找当前目录及子目录下所有名为test_*.py或*_test.py的文件并执行其中所有以test_开头的函数。让我们创建一个最简单的测试文件。在你的项目根目录下新建一个名为test_sample.py的文件# test_sample.py def inc(x): 一个简单的递增函数用于演示。 return x 1 def test_answer(): 测试 inc 函数。 result inc(3) assert result 5 # 这里我们故意写一个错误的断言这个测试注定会失败因为inc(3)返回4而我们断言它等于5。但这正是我们想要的——看看pytest如何报告失败。在包含test_sample.py的目录下直接运行pytest你会看到类似下面的彩色输出在支持颜色的终端中 test session starts platform darwin -- Python 3.9.0, pytest-8.0.0, pluggy-1.0.0 rootdir: /path/to/your/project collected 1 item test_sample.py F [100%] FAILURES _________________________________ test_answer _________________________________ def test_answer(): result inc(3) assert result 5 E assert 4 5 test_sample.py:8: AssertionError short test summary info FAILED test_sample.py::test_answer - assert 4 5 1 failed in 0.02s 这个输出信息量很大测试会话开始显示了Python和pytest版本、项目根目录。收集到的测试项collected 1 item表示找到了一个测试函数。测试进度F表示失败Fail[100%]是进度条。详细的失败信息这是pytest的杀手锏。它不仅告诉你assert 4 5失败了还通过E行下面的缩进提示清晰地展示了4是inc(3)计算的结果。这种“断言重写”机制让你无需使用复杂的断言方法就能获得清晰的调试信息。简短的测试摘要快速总结哪些测试失败了。总耗时整个测试会话运行的时间。现在我们把断言改正确assert result 4再次运行pytest你会看到.表示通过以及1 passed in 0.01s的绿色成功提示。2.3 核心命令行选项入门仅仅一个pytest命令远不是全部。下面几个选项是你几乎每次都会用到的-v/--verbose: 输出更详细的信息包括每个测试用例的名字而不是简单的点或F。pytest -v # 输出: test_sample.py::test_answer PASSED-q/--quiet: 简化输出只显示最终结果摘要适合在CI/CD日志中减少噪音。-k 通过关键字表达式筛选要运行的测试。例如只运行名称中包含answer的测试pytest -k answer表达式支持and,or,not。例如pytest -k “not slow”会跳过所有名字里带slow的测试。-x 遇到第一个失败或错误时立即停止测试。这在调试时非常有用你不需要等所有测试跑完。--lf/--last-failed: 只重新运行上一次失败的测试。结合-x使用调试效率极高。--tbstyle: 控制失败回溯信息的详细程度。常用选项有--tbshort: 只显示失败位置的简短回溯非常清晰。--tbline: 每个失败只显示一行信息。--tbno: 不显示回溯信息只显示摘要。我个人在调试时喜欢用--tbshort在CI流水线中为了日志简洁可能用--tbline。3. 测试代码组织与断言3.1 测试文件与测试类的组织虽然pytest支持纯函数式测试但在组织大量相关测试时使用测试类是一个好习惯它能更好地进行逻辑分组和共享装置。测试文件命名如前所述test_*.py或*_test.py。测试函数命名以test_开头。测试类命名以Test开头且不能有__init__方法。这是pytest发现测试类内测试方法的规则。# test_math_operations.py class TestMathOperations: 测试数学运算的类。 def test_addition(self): assert 1 1 2 def test_subtraction(self): assert 5 - 3 2 def test_multiplication(): 类外部的测试函数。 assert 2 * 3 6运行pytest -v你会看到三个测试项TestMathOperations::test_additionTestMathOperations::test_subtraction 和test_multiplication。实操心得对于小型项目或工具库按模块组织测试文件即可例如utils.py对应test_utils.py。对于大型项目比如一个Web应用我倾向于按功能或层级组织比如tests/unit/test_models.py,tests/unit/test_services.py,tests/integration/test_api.py。使用pytest时目录结构非常自由它都能正确发现。3.2 强大的断言机制pytest的断言之所以强大全靠其“断言重写”机制。你写的普通assert语句在测试执行前会被pytest解析并重写加入详细的比较信息。看看这些例子感受一下它的威力def test_complex_assertions(): # 比较列表 expected_list [1, 2, 3] result_list [1, 2, 4] # assert result_list expected_list # 失败时会漂亮地显示[1, 2, 4] [1, 2, 3] # ^ # 检查异常 import pytest with pytest.raises(ValueError) as exc_info: int(not a number) # 可以进一步检查异常信息 assert invalid literal in str(exc_info.value) # 检查近似相等对于浮点数非常有用 assert 0.1 0.2 pytest.approx(0.3) # 检查集合成员或子集 assert {a, b}.issubset({a, b, c}) # 或者用更pytest的风格需要了解一点插件或自定义但更直观的写法是直接用集合方法 # 检查字典包含关系 expected {name: Alice, age: 30} result {name: Alice, age: 30, city: NYC} assert expected.items() result.items() # 检查expected是result的子集pytest对于常见容器类型list, dict, set、字符串的比较在失败时会进行智能的差异对比diff直接高亮出第一个不同的元素这比unittest的assertEqual输出友好太多了。3.3 使用pytest.mark对测试进行分类标记当你的测试套件变得庞大你可能需要将测试分类比如慢速测试、集成测试、冒烟测试等。pytest的标记系统pytest.mark就是为此而生。首先你需要在项目根目录或tests目录下创建一个pytest.ini配置文件来注册你的自定义标记避免运行时警告# pytest.ini [pytest] markers slow: marks tests as slow (deselect with -m “not slow”) integration: marks tests as integration tests smoke: marks tests as smoke tests然后在你的测试代码中就可以使用这些标记了import pytest import time pytest.mark.slow def test_very_slow_api_call(): 这是一个模拟的慢速API调用测试。 time.sleep(2) # 模拟耗时操作 assert True pytest.mark.integration def test_database_connection(): 这是一个集成测试需要外部数据库。 # ... 连接数据库的代码 assert True pytest.mark.smoke class TestSmokeSuite: 冒烟测试套件。 def test_login(self): assert True def test_homepage(self): assert True # 可以组合标记 pytest.mark.slow pytest.mark.integration def test_comprehensive_analysis(): assert True运行测试时你可以用-m选项来筛选pytest -m smoke 只运行标记为smoke的测试。pytest -m “not slow” 运行所有非慢速测试。pytest -m “integration or smoke” 运行集成测试或冒烟测试。注意事项标记名不要使用pytest的内置关键字如parametrize,fixture等。自定义标记在pytest.ini中注册是一个好习惯否则运行时会看到PytestUnknownMarkWarning警告。在CI流水线中我通常会配置不同的任务一个快速任务运行-m “not slow”一个夜间任务运行所有测试包括-m slow。4. 测试装置Fixtures深度解析Fixtures是pytest的灵魂是它相较于unittest的最大优势之一。它提供了一种强大、灵活的方式来为测试准备环境、提供数据和进行清理。4.1 什么是Fixture你可以把 Fixture 理解为一个“测试装置”或“测试资源”。它是在测试运行之前或之后执行的一段代码用于设置测试前提条件和清理测试环境。它的核心目标是消除重复代码和实现测试间的隔离。4.2 定义与使用一个简单的FixtureFixture 通过pytest.fixture装饰器定义。测试函数可以通过将 Fixture 的函数名作为参数来请求使用它。import pytest pytest.fixture def sample_data(): 提供一个简单的数据列表装置。 print(\n(Setting up sample_data)) data [1, 2, 3, 4, 5] yield data # 测试执行时会从这里拿到 data print(\n(Cleaning up sample_data)) # yield 之后的代码是清理部分无论测试成功与否都会执行 def test_sum(sample_data): # 通过参数名请求 fixture 测试求和。 total sum(sample_data) assert total 15 print(Running test_sum) def test_max(sample_data): 测试最大值。 max_val max(sample_data) assert max_val 5 print(Running test_max)运行pytest -v -s(-s用于允许打印输出)你会看到类似以下的执行顺序(Setting up sample_data) Running test_sum (Cleaning up sample_data) (Setting up sample_data) Running test_max (Cleaning up sample_data)注意sample_datafixture 为每个测试函数都执行了一次设置和清理。这是默认的scope”function”的作用域。4.3 Fixture的作用域Scope作用域决定了Fixture在何时被创建和销毁。pytest支持5种作用域作用域描述适用场景function(默认)每个测试函数运行一次。最常用用于提供独立的测试数据或临时状态。class每个测试类运行一次被该类中的所有测试方法共享。当一个类中的所有测试需要相同的、昂贵的设置时。module每个测试模块.py文件运行一次被该模块中的所有测试共享。模块级别的全局设置如创建临时文件、启动模拟服务器。package每个测试包目录运行一次。相对少用用于包级别的资源。session一次测试会话即一次pytest命令执行只运行一次。最昂贵的资源如启动数据库容器、登录获取全局Token。import pytest import time pytest.fixture(scope”module”) def expensive_resource(): 模拟一个创建成本很高的资源比如数据库连接池。 print(“\n Creating expensive resource (module scope)”) resource {“db_conn”: “模拟连接”, “created_at”: time.time()} yield resource print(“\n Closing expensive resource (module scope)”) resource.clear() def test_query_a(expensive_resource): print(f”Test A using {expensive_resource}“) assert “db_conn” in expensive_resource def test_query_b(expensive_resource): print(f”Test B using {expensive_resource}“) # 注意两个测试使用的是同一个 resource 对象 # 如果test_query_a修改了它会影响test_query_b。这就是共享状态的风险。 assert expensive_resource[“db_conn”] “模拟连接”运行后你会看到Creating...和Closing...只打印了一次尽管有两个测试使用了它。重要提示谨慎使用module和session作用域。共享的Fixture意味着测试之间可能产生状态依赖破坏了测试的独立性这是自动化测试的大忌。除非该资源创建成本极高如启动Docker容器且你确信测试不会修改其共享状态否则优先使用function作用域。4.4 Fixture的自动使用autouse有些Fixture你希望在某些作用域内自动执行而不需要每个测试函数都显式声明为参数。这时可以用autouseTrue。import pytest pytest.fixture(autouseTrue, scope”session”) def global_setup_teardown(): 会话级别的自动装置用于所有测试。 print(“\n SESSION SETUP ) yield print(“\n SESSION TEARDOWN ) pytest.fixture(autouseTrue) def function_level_logging(): 函数级别的自动装置用于记录每个测试。 print(“\n--- Test function starting ---”) yield print(“\n--- Test function finished ---”) def test_one(): print(“Inside test_one”) assert True def test_two(): print(“Inside test_two”) assert True运行后你会看到自动装置按顺序执行。自动装置非常适合于全局的日志配置、监控打点、或强制性的环境检查。4.5 Fixture的依赖注入与嵌套Fixture的强大之处还在于它们可以相互依赖。一个Fixture可以请求另一个Fixturepytest会自动解析这些依赖关系并按正确的顺序执行。import pytest import tempfile import os pytest.fixture def temp_dir(): 创建一个临时目录。 with tempfile.TemporaryDirectory() as tmpdir: print(f”Created temp dir: {tmpdir}“) yield tmpdir # TemporaryDirectory 的上下文管理器会自动清理 pytest.fixture def sample_file(temp_dir): # 这个fixture依赖于temp_dir 在临时目录中创建一个样本文件。 file_path os.path.join(temp_dir, “data.txt”) with open(file_path, ‘w’) as f: f.write(“Hello, pytest!\n”) print(f”Created sample file: {file_path}“) yield file_path # 文件会被随目录一起清理所以这里不需要额外操作 def test_file_content(sample_file): # 测试直接请求最终需要的sample_file 测试文件内容。 with open(sample_file, ‘r’) as f: content f.read() assert content “Hello, pytest!\n” print(f”Verified content of {sample_file}“)在这个例子中执行test_file_content时pytest会先执行temp_dir再执行sample_file最后才运行测试函数。依赖关系清晰资源管理自动化。5. 参数化测试用一份代码测试多组数据当你需要对同一个测试逻辑使用多组不同的输入和期望输出来验证时逐一定义多个测试函数是低效且难以维护的。pytest的pytest.mark.parametrize装饰器完美解决了这个问题。5.1 基本参数化import pytest # 定义一个简单的函数用于测试 def add(a, b): return a b # 使用 parametrize 装饰器 pytest.mark.parametrize(“a, b, expected”, [ (1, 2, 3), # 第一组数据 (4, 5, 9), # 第二组数据 (10, -5, 5), # 第三组数据 (0, 0, 0), # 第四组数据 ]) def test_add(a, b, expected): 测试加法函数的多组数据。 result add(a, b) assert result expected运行pytest -v你会看到四个独立的测试项test_param.py::test_add[1-2-3] PASSED test_param.py::test_add[4-5-9] PASSED test_param.py::test_add[10--5-5] PASSED test_param.py::test_add[0-0-0] PASSEDpytest为每一组数据都生成了一个独立的测试用例并且失败时能精确地定位到是哪一组数据出了问题。5.2 参数化与Fixture结合参数化也可以和Fixture一起使用实现更动态的数据生成。import pytest pytest.fixture(params[“alice”, “bob”, “charlie”]) def username(request): # request 是一个内置fixture用于访问参数 一个参数化的fixture提供多个用户名。 return request.param def test_username_length(username): 测试每个用户名的长度都大于3。 assert len(username) 3 print(f”Checking username: {username}“)这个测试会运行三次每次usernamefixture 提供不同的值。5.3 解决热词中的问题参数化导致标题换行你搜索到的热词中有一个非常具体的问题“有用例标题和参数时。标题会被参数挤得换行怎么解决”。这指的是当使用参数化并且参数值较长或较多时在测试报告特别是Allure报告或pytest -v的输出中测试ID会变得很长导致换行影响可读性。解决方案是为参数化用例设置自定义的ID。pytest.mark.parametrize装饰器接受一个ids参数它是一个字符串列表或可调用对象用于为每一组参数生成一个简短的标识符。import pytest def compute(data): # 模拟复杂计算 return data * 2 # 假设我们有很多复杂的参数 test_data [ ([1, 2, 3], [2, 4, 6]), # 简单列表 ({“a”: 1}, {“a”: 2}), # 字典 (“long_string_value_here”, “long_string_value_here” * 2), # 长字符串 ] # 不指定ids输出会很乱 pytest.mark.parametrize(“input_data, expected”, test_data) def test_compute_bad(input_data, expected): result compute(input_data) assert result expected # 使用ids参数优化 def id_func(val): 根据参数生成简短的ID。 if isinstance(val, list): return “list” elif isinstance(val, dict): return “dict” elif isinstance(val, str): return “str” else: return str(val) pytest.mark.parametrize(“input_data, expected”, test_data, idsid_func) def test_compute_good(input_data, expected): result compute(input_data) assert result expected运行pytest test_param.py::test_compute_good -v输出会是test_param.py::test_compute_good[list] PASSED test_param.py::test_compute_good[dict] PASSED test_param.py::test_compute_good[str] PASSED清晰多了在Allure报告中也会使用这些ID使得报告更加整洁。ids也可以直接是一个字符串列表ids[“case1”, “case2”, “case3”]。6. 常用插件与高级配置pytest的生态系统是其成功的关键。通过插件你可以轻松扩展其功能。下面介绍几个几乎成为“标配”的插件。6.1 pytest-html生成HTML报告虽然pytest自带的终端输出已经很棒但一份美观的HTML报告更适合分享和归档。安装pip install pytest-html使用pytest --htmlreport.html这会在当前目录生成一个report.html文件包含测试概述、通过/失败详情、日志输出等。你还可以通过--self-contained-html生成一个包含所有CSS/JS的单文件报告方便传输。6.2 pytest-cov集成测试覆盖率测试覆盖率是衡量测试完备性的重要指标。pytest-cov插件可以无缝集成coverage.py。安装pip install pytest-cov使用# 测量整个项目的覆盖率 pytest --covmy_project # 测量特定模块的覆盖率 pytest --covmy_project.utils # 生成HTML格式的覆盖率报告 pytest --covmy_project --cov-reporthtml运行后会在终端输出摘要并生成htmlcov目录打开index.html可以交互式地查看哪些代码行被覆盖了。6.3 pytest-xdist并行运行测试当测试套件很大时串行运行会非常耗时。pytest-xdist插件可以实现测试的并行执行充分利用多核CPU。安装pip install pytest-xdist使用# 使用所有可用的CPU核心 pytest -n auto # 指定使用2个worker进程 pytest -n 2并行化能显著缩短测试执行时间。但需要注意测试必须是独立的不能有共享状态或依赖执行顺序否则并行会导致随机失败。通常合理使用function作用域的Fixture可以保证独立性。6.4 配置文件 pytest.ini我们之前简单提到了pytest.ini用于注册标记。实际上它是一个强大的中心化配置工具。通常放在项目根目录。一个功能更全的pytest.ini示例# pytest.ini [pytest] # 1. 自定义标记 markers slow: marks tests as slow. integration: marks tests as integration test (requires external services). smoke: marks tests as smoke test suite. # 2. 测试文件/目录的查找规则 testpaths tests # 告诉pytest只在tests目录下查找测试 python_files test_*.py # 匹配测试文件默认 python_classes Test* # 匹配测试类默认 python_functions test_* # 匹配测试函数默认 # 3. 命令行默认选项 addopts -v # 默认详细模式 --strict-markers # 对未注册的标记发出错误而非警告 --tbshort # 默认使用简短的回溯信息 # -x # 可以根据需要开启但通常不在默认配置因为CI需要跑完全部 # 4. 忽略特定警告 filterwarnings ignore::DeprecationWarning # 忽略所有DeprecationWarning # ignore:.*U.*mode is deprecated:FutureWarning # 忽略特定FutureWarning # 5. 设置最低匹配的测试项数用于检测是否收集到测试 minversion 6.0 # 指定pytest最低版本通过pytest.ini你可以统一团队或项目的测试运行风格减少每次在命令行输入冗长参数的需要。7. 与unittest和doctest的兼容如果你有一个历史项目使用的是unittest迁移到pytest几乎是无痛的。pytest可以直接运行unittest风格的测试用例。7.1 运行unittest测试假设你有一个unittest测试文件# test_unittest_example.py import unittest class TestOldSuite(unittest.TestCase): def test_upper(self): self.assertEqual(‘foo’.upper(), ‘FOO’) def test_isupper(self): self.assertTrue(‘FOO’.isupper()) self.assertFalse(‘Foo’.isupper())直接使用pytest命令运行它pytest test_unittest_example.py -vpytest能够发现继承自unittest.TestCase的类并运行其中的test_方法。同时你还可以享受pytest的很多特性比如-k筛选、-x快速失败等。不过unittest中setUp/tearDown方法依然有效但更推荐逐步迁移到pytest的 fixture 模式。7.2 运行doctestdoctest是Python标准库的一部分允许你将测试写在文档字符串中。pytest也原生支持运行doctest。假设你有一个模块mymath.py# mymath.py def add(a, b): “”“ Add two numbers. add(2, 3) 5 add(-1, 1) 0 ”“” return a b你可以使用pytest的--doctest-modules选项来运行这些文档测试pytest --doctest-modules mymath.py或者如果你想对所有模块运行doctestpytest --doctest-modules .这对于确保代码示例和文档始终正确非常有用。8. 常见问题排查与实战技巧即使掌握了所有功能在实际项目中还是会踩坑。下面分享一些高频问题和解决技巧。8.1 测试发现失败找不到测试问题运行pytest后显示collected 0 items。排查检查文件/函数命名确保测试文件以test_开头或结尾测试函数/方法以test_开头测试类以Test开头。检查当前目录确保你在正确的目录下运行命令。使用pytest /path/to/tests指定路径。检查__init__.py文件如果你的测试放在一个包里例如tests/目录确保该目录下有一个__init__.py文件可以是空的这样Python才会将其视为一个包pytest才能正确导入。对于Python 3.3这不是强制的但有时能解决导入问题。检查pytest.ini配置确认testpaths或norecursedirs配置没有排除你的测试目录。使用pytest --collect-only这个命令会显示pytest发现了哪些测试项而不实际运行它们。这是调试测试发现问题的利器。8.2 ImportError: 无法导入被测模块问题测试文件运行时提示ModuleNotFoundError: No module named ‘my_module’。原因Python的模块导入路径sys.path没有包含你的项目根目录。解决推荐方案在项目根目录下运行pytest。pytest会自动将当前目录运行命令的目录添加到sys.path的开头。设置PYTHONPATH在运行前设置环境变量export PYTHONPATH/path/to/your/project:$PYTHONPATHLinux/macOS或set PYTHONPATHC:\path\to\your\projectWindows。使用pytest的--import-modepytest --import-modeimportlib是较新的导入方式有时能更好地处理路径问题。使用sys.path修改在测试文件或conftest.py开头添加import sys import os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ‘..’)))但这种方法不够优雅且可能带来副作用。8.3 Fixture作用域与状态污染问题某个测试莫名其妙地失败了但单独运行又是好的。这很可能是由于module或session作用域的Fixture被其他测试修改导致了状态污染。解决优先使用function作用域这是保证测试独立性的最安全方式。对于共享Fixture确保其状态不可变或可重置如果必须共享确保返回的数据是不可变的如元组、冻结集合或者在yield之后、下一个测试使用之前有明确的复位逻辑。使用pytest的--tbshort和-x快速定位结合--lf重新运行失败测试观察是否在特定顺序下才失败。对可能被修改的共享资源进行深拷贝import copy pytest.fixture(scope”module”) def shared_config(): config {“key”: “original_value”} yield copy.deepcopy(config) # 每次yield一个副本 # 注意这不能防止测试修改yield后持有的那个副本但能保证每个测试拿到的是初始副本。8.4 测试依赖外部服务网络、数据库问题测试需要连接数据库、调用第三方API等导致测试不稳定、速度慢。策略模拟Mock使用unittest.mock模块Python标准库或pytest-mock插件来模拟外部依赖。这是单元测试的首选。import pytest from unittest.mock import Mock, patch import requests def test_fetch_data(mocker): # pytest-mock 提供的 fixture # 模拟 requests.get 返回一个特定响应 mock_response Mock() mock_response.json.return_value {“data”: “test”} mocker.patch(‘requests.get’, return_valuemock_response) # 现在调用被测函数它内部的 requests.get 会被模拟 # ... 你的测试逻辑使用测试专用服务对于集成测试可以使用Docker启动一个临时的数据库如PostgreSQL、Redis并在session级别的Fixture中管理其生命周期。可以使用pytest-docker等插件简化流程。标记与跳过将这些依赖外部服务的测试标记为pytest.mark.integration并在没有外部环境的CI流水线或开发环境中用-m “not integration”跳过它们。8.5 测试输出太多或太少问题测试日志、print语句干扰了pytest的输出或者你想看到更多内部日志。控制输出捕获输出pytest默认会捕获所有标准输出和标准错误测试通过时不显示。使用-s或--captureno禁用捕获这样所有print和日志都会显示。控制日志级别在pytest.ini中配置[pytest] log_cli true log_cli_level INFO这样会在终端实时显示INFO及以上级别的日志。使用caplogfixture专门用于在测试中捕获和断言日志输出。import logging def test_logging(caplog): caplog.set_level(logging.INFO) logging.info(“This is an info message”) assert “This is an info message” in caplog.text8.6 性能优化选择性运行与缓存pytest -k 如前所述用关键字筛选。pytest -m 用标记筛选。pytest --lf 只运行上次失败的。pytest --ff 先运行上次失败的然后运行其余的。pytest的缓存pytest会自动在项目根目录的.pytest_cache目录中缓存上次测试运行的信息如失败列表。--lf和--ff就依赖于这个缓存。你可以通过pytest --cache-show查看缓存内容或pytest --cache-clear清空缓存。掌握这些技巧你就能像一位经验丰富的测试工程师一样高效地利用pytest构建健壮、可维护的自动化测试体系。从简单的函数测试到复杂的多系统集成测试pytest的这套组合拳都能应对自如。记住好的测试不是写出来的而是在不断重构和实践中演化出来的。pytest提供的这些工具正是为了支持这种演化而生的。

相关新闻

AI视频三引擎对比:Runway、Veo 3与MidJourney创作人格解析

AI视频三引擎对比:Runway、Veo 3与MidJourney创作人格解析

1. 项目概述:当同一组画面撞上三款AI视频引擎,故事就分了岔路 我试过用AI生成一张图——那感觉像在调色盘上点了一滴颜料,结果它自己晕染成整幅水彩。但当我第一次把同一组精心绘制的超现实沙漠场景图,分别喂给Runway Gen-4、Goog…

2026/7/4 16:24:45 阅读更多 →
WebAuthn与FIDO2实战指南:从原理到代码实现无密码登录

WebAuthn与FIDO2实战指南:从原理到代码实现无密码登录

1. 项目概述:为什么我们需要告别密码? 如果你和我一样,每天需要在十几个不同的网站和应用之间切换,每次登录都要在记忆里翻找那个“大小写字母数字特殊符号”的组合,或者焦急地等待手机上的验证码,那你一定…

2026/7/4 16:22:44 阅读更多 →
Wwise音频工具终极指南:3分钟掌握游戏音频文件解包与定制技巧

Wwise音频工具终极指南:3分钟掌握游戏音频文件解包与定制技巧

Wwise音频工具终极指南:3分钟掌握游戏音频文件解包与定制技巧 【免费下载链接】wwiseutil Tools for unpacking and modifying Wwise SoundBank and File Package files. 项目地址: https://gitcode.com/gh_mirrors/ww/wwiseutil 你是否曾经想过修改游戏中的…

2026/7/4 16:20:44 阅读更多 →

最新新闻

Go语言JWT认证实战:从原理到生产级安全实现

Go语言JWT认证实战:从原理到生产级安全实现

1. 项目概述:为什么Go和JWT是API安全的黄金搭档最近在重构一个微服务项目,认证模块的选型又让我重新审视了一遍JWT。说实话,在Go语言生态里做API认证,JWT几乎成了默认选项,但真正能把它用“安全”的团队并不多。大部分…

2026/7/4 17:10:57 阅读更多 →
嵌入式系统三重降压转换方案设计与优化

嵌入式系统三重降压转换方案设计与优化

1. 为什么需要三重降压转换方案在嵌入式系统和低功耗设备开发中,多电压域供电一直是个棘手问题。我最近接手的一个工业控制器项目就遇到了典型场景:主控MCU需要3.3V核心电压,传感器模块要求1.8V工作电压,而外围接口又得维持5V电平…

2026/7/4 17:10:57 阅读更多 →
基于YOLOv8的番茄叶片病变识别系统设计与实现

基于YOLOv8的番茄叶片病变识别系统设计与实现

1. 项目概述这个基于YOLOv8的番茄叶片病变识别系统是我在毕业设计期间完成的一个实用项目。作为一名计算机视觉方向的毕业生,我选择将深度学习技术应用于农业领域,解决传统病害检测方法效率低下的问题。系统能够自动识别番茄叶片上的多种常见病害&#x…

2026/7/4 17:08:57 阅读更多 →
Transformers.js终极指南:如何在浏览器中运行AI模型而无需服务器支持

Transformers.js终极指南:如何在浏览器中运行AI模型而无需服务器支持

Transformers.js终极指南:如何在浏览器中运行AI模型而无需服务器支持 【免费下载链接】transformers.js State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server! 项目地址: https…

2026/7/4 17:08:57 阅读更多 →
QRazyBox终极指南:5分钟学会修复损坏二维码的完整教程

QRazyBox终极指南:5分钟学会修复损坏二维码的完整教程

QRazyBox终极指南:5分钟学会修复损坏二维码的完整教程 【免费下载链接】qrazybox QR Code Analysis and Recovery Toolkit 项目地址: https://gitcode.com/gh_mirrors/qr/qrazybox 你是否遇到过这样的烦恼?重要的二维码因为打印模糊、表面划痕或图…

2026/7/4 17:06:57 阅读更多 →
如何在Windows和Linux上获得完整的AirPods体验:免费开源工具终极指南

如何在Windows和Linux上获得完整的AirPods体验:免费开源工具终极指南

如何在Windows和Linux上获得完整的AirPods体验:免费开源工具终极指南 【免费下载链接】AirPodsDesktop ☄️ AirPods desktop user experience enhancement program, for Windows and Linux (WIP) 项目地址: https://gitcode.com/gh_mirrors/ai/AirPodsDesktop …

2026/7/4 17:04:56 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻