python之pytest学习笔记
- test的本质:
- Arrange
- Act
- Assert
- Cleanup
- pytest执行测试用例的一般步骤:
- 测试发现:pytest从命令行或代码中指定的位置开始,查找以test_开头的文件、类和函数。
- Fixture解析:pytest分析fixture依赖关系,构建依赖图。对于autouse fixture,它会自动应用到其作用域内的所有测试,而不需要测试函数显式声明。
- 测试执行:对于每个测试项,pytest按照fixture的作用域和依赖关系执行setup、测试调用和teardown。
- 如何调用Pytest
- 指定特定的测试用例
- 模块(.py文件)、目录、关键字、类、函数方法、特定参数的函数方法
- 特定标签
- 导入包名、读取txt文件
- 执行时间最慢的文件--slow
- 预加载内、外部插件运行
- 使用python 命令
- pytest.main在代码内部调用
- 查看选中的测试用例:pytest --collect-only
- 如何编写和报告断言
- 使用断言语句断言:assert f() == 4
- 近似相等的断言:pytest.approx()
- 预期异常的断言: pytest.raises()
- 匹配异常消息 with pytest.raises(ValueError, match=r".* 123 .*")
- 异常组的断言:with pytest.RaisesGroup(ValueError) 它允许将多个异常包装成一个单一的“组异常”一起抛出。这就像一个“异常容器”,可以同时携带多个不同类型的异常。
- 传统异常模型:“一个错误,一个异常”。
- 新的异常组模型:“一次操作,多个异常;一并抛出,统一处理”。
- xfail 标记类异常 pytest.mark.xfail
- 预期警告的断言: pytest.warns
- 上下文比较类(context-sensitive)的断言:assert set1 == set2
- 自定义失败断言:pytest_assertrepr_compare(config, op, left, right)
- 重写断言并存入缓存:register_assert_rewrite 、pyc files
- 如何使用属性标记测试函数
- 需注册标签(最好限制为强制注册)
- 内置标签:
- usefixtures - use fixtures on a test function or class @pytest.mark.usefixtures("cleandir")
- filterwarnings - filter certain warnings of a test function @pytest.mark.filterwarnings("ignore:api v1")
- skip - always skip a test function @pytest.mark.skip(reason="no way of currently testing this")
- skipif - skip a test function if a certain condition is met @pytest.mark.skipif(sys.version_info < (3, 13), reason="requires python3.13 or higher")
- xfail - produce an “expected failure” outcome if a certain condition is met @pytest.mark.xfail
- parametrize - perform multiple calls to the same test function. @pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])
- 关于Fixture
- 它的主要作用是创建可复用的测试数据和准备测试环境,并以“依赖注入”的方式将它们提供给测试函数。
- In testing, a fixture provides a defined, reliable and consistent context for the tests. 主要是提供上下文环境,即Arrange和Cleanup的部分
- 为什么使用 Fixture?(它的巨大价值)
- 消除重复代码:如果多个测试用例都需要“苹果”和“果篮”,你只需要定义一次 fixture,然后在不同测试中声明使用即可。修改数据只需修改一处。
- 逻辑清晰,关注点分离:测试函数内部只关注测试逻辑(断言 my_fruit in fruit_basket),而不需要关心数据是如何构造的。数据构造的细节被封装在 fixture 中。
- 高效的依赖管理:如图中所示,fixture 之间可以相互依赖(fruit_basket依赖 my_fruit)。pytest 会自动处理这些依赖关系,并按正确的顺序执行它们,构建出复杂的测试场景。
- 灵活性高:你可以通过组合不同的 fixture 来轻松构建各种测试场景,比如一个测试用“苹果”,另一个测试可以用“橘子”,只需创建不同的 fixture 或进行参数化。
- 可以使用第三方库来加载测试数据,如pytest-datadir、 pytest-datafiles
- https://pypi.org/project/pytest-datadir/
- https://pypi.org/project/pytest-datafiles/
- 如何使用Fixture
- 作为被测函数中的一个参数声明来使用fixture
- fixture 函数也可以通过声明参数来使用另一个 fixture
- 同一个fixture可以被调用多次
- 一个test函数/fixture可以一次调用多个fixtures
- 在模块和类中有多种调用方式。如用mark标签来调用多个fixture @pytest.mark.usefixtures("cleandir", "anotherfixture")
- 使用pytest_plugins = "mylibrary.fixtures"来调用外部的fixture
- pytest fixture缓存机制
- 你可以把每个 fixture 在一个测试函数内的生命周期,理解为一个 “单例”或 “一次性计算”。
- 核心规则是:在同一个测试函数执行过程中,同一个 fixture 只会被实际执行一次,其返回值会被缓存起来,后续所有对该 fixture 的请求都会直接拿到这个缓存值,而不会重复执行 fixture 函数。
- 所有依赖方访问的是同一个缓存对象
- 缓存机制的关键价值:
- 保证状态一致性:这是最重要的好处。所有依赖方操作的是同一个对象。在这个例子中,append_firstfixture 修改了 order列表,测试函数中请求的 order看到的是被修改后的同一个列表。
- 提升性能:如果 fixture 执行的是创建数据库连接、启动浏览器等耗时操作,缓存机制避免了重复执行,极大提升了测试速度。
- 避免副作用:如果 fixture 包含不可重复的操作(例如,在数据库中插入一条唯一记录),缓存机制防止了因重复执行而导致的错误。
- Autouse fixtures
- Autouse fixtures 是那些你无需在测试函数参数中显式声明,但 pytest 会自动为每个测试函数执行的 fixture
- 普通 Fixture:测试函数需要“主动请求”(通过函数参数声明)才会执行。
- Autouse Fixture:只要它被定义,就会“自动运行”,作用于其作用域内的所有测试。
- 特别适合用于那些全局性、基础性的设置和清理工作
- 测试数据初始化/清理
- 全局 Mock
- 临时目录/环境设置
- 重要注意事项与最佳实践:
- 谨慎使用:不要滥用 autouse。只有真正被所有或绝大多数测试需要的 fixture 才应该设置为 autouse=True。滥用会使测试行为变得不透明,难以理解测试的真正依赖。
- 作用域管理:使用 scope参数(如 scope="session", scope="module")来控制 autouse fixture 的执行频率,避免不必要的重复设置。
- 避免状态残留:如图中示例所示,append_first修改了 order的状态,影响了后续的 test_string_and_int。在测试中,应极力避免这种有状态的、会产生副作用的 autouse fixture,因为它破坏了测试的独立性,导致测试结果不可预测且难以调试。每个测试都应该是隔离的。
- 固定作用域 :scope="module"
![]()
- autouse fixture在其作用域内自动执行。对于class作用域,它会在类的测试方法之前执行一次。
![]()
- 动态作用域:Dynamic scope @pytest.fixture(scope=determine_scope)
- 参数化的Fixture
![]()
- 当使用 params参数化一个 fixture 时,pytest 会为 params列表中的每一个值都创建并缓存一个独立的 fixture 实例。
- 参数化可以自定义ids,来方便定位问题。
- 参数化时,也可以单独为某个参数来打标签
- 使用 pytest_generate_tests钩子(hook)进行动态参数化
- 参数化执行时,testcase会自动分组:(Automatic grouping)
- 一个参数一个参数的运行的,即一个参数运行setup并且teardown之后,才会再运行下一个参数。
- 同一个测试case中,先执行最大的scope参数,将scope参数内的其他参数组合遍历执行完毕后,再用最大参数的其他scope参数继续遍历。
- 使用最少的测试资源来将所有case遍历完全。但仍旧会先teardown上一个参数后,才会setup下一个参数。“参数化的模块作用域 modarg 资源导致了测试执行顺序的调整,从而实现了尽可能少的“活跃”资源。
- subtests是 pytest 的一个内置 fixture
- Teardown/Cleanup
- 使用yield fixture
- 执行顺序:fixtureA---yieldA---fixtureB---yieldB---teardownB--teardownA的顺序执行
- the first teardown code to run is from the right-most fixture
- 在yieldA之前报错,则不再执行teardownA
- 终止teardown时最先运行的是最后一个yield之后的teardown
- 即使执行顺序正确,但也有可能会本身报错,故需要safe teardowns
- 使用终止器(“finalizer” functions)
- first-in-last-out,无论之前有没有报错,均会执行。
- request.addfinalizer(empty_mailbox)
- 终止时的顺序最先运行的,是最后调用request.addfinalizer的
- Safe fixture structure
- 要保证fixture 结构设计的原子性,即每一个都可独立建立,独立拆卸。隔离性、原子性、复用性、一致性都会更好。
- 每个fixture都是一个独立的、可复用的模块。
- 测试的真正动力和依赖来源是 pytest 的 fixture 系统,它通过参数注入的方式为测试提供所需的一切,而不是通过类的实例属性(self.xxx)来传递状态。
- Fixture可以使用request对象检查测试用例的上下文环境
- 使用request对象 def smtp_connection(request): server = getattr(request.module, "smtpserver", "smtp.gmail.com")
- 利用标记(Markers)将数据从测试函数“向上”传递到 fixture。
- “使用标记向固件传递数据”的规则,本质上是开辟了一条从测试用例到其依赖环境的“上行”通信渠道。它通过 request对象和自定义标记,实现了测试函数对 fixture 行为的精细化控制,是构建高度灵活和可复用测试基础设施的强大工具。@pytest.mark.fixt_data(42)
- Fixture可以直接return一个函数工厂而不只是单纯的一个值
- 覆盖重写Fixture
- 文件级别,子文件夹同名覆盖
- 模块中重定义同名的fixture 函数
- 测试函数参数化重定义fixture: @pytest.mark.parametrize('username', ['directly-overridden-username'])
- tmp_path fixture
- 为每一个测试函数提供一个临时文件夹
- tmp_path_factory是一个session级别的fixture,可用于从任何其他 fixture 或测试中创建任意临时目录。
- Monkeypatching动态补丁
- 在程序运行时动态地修改或替换代码中的属性、方法、类甚至模块,以达到测试的目的。
- 为了隔离外部依赖,使用场景有:mock函数、mock返回对象、全局patch、环境变量和字典的修改
- doctests“文档即测试”
- Doctest 是 Python 的一个标准库模块,它允许你将可执行的测试用例直接嵌入在代码的文档字符串(docstring)中。
- 通过 pytest --doctest-modules命令,提供了更强大、更灵活的 doctest 执行能力。
![]()
- 重跑失败的用例 re-run failed tests
- --lf, --last-failed - to only re-run the failures.
- --ff, --failed-first - to run the failures first and then the rest of the tests.
- 缓存的使用及清除
- pytestconfig.cache.get("example/value", None)
- pytest --cache-show
- pytest --cache-clear
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/970110.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!