当前位置: 首页 > news >正文

在 Python Pytest 中传递函数到测试文件

原文:towardsdatascience.com/passing-functions-to-test-files-in-python-pytest-0f5fe9efed5d

PYTHON 编程

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f1a41dfb22cac418f5f2d8e4d634377.png

夹具有助于在测试函数中重用对象,包括函数。图片由rivage在Unsplash提供

当您使用 Pytest 进行 Python 单元测试时,您可以使用在conftest.py文件中定义的 Pytest 夹具将对象传递到测试文件中。我使用了复数形式,因为您可以定义任意数量的conftest.py文件,它们的位置定义了它们的范围。

当我刚开始学习 Python 单元测试时,我经常想知道如何定义一个辅助函数用于测试,并在选定的测试函数中使用它。如果您只需要在一个测试文件中使用这个函数,没问题:只需在这个文件中定义该函数,然后就可以使用了。

如果您需要在多个测试文件中使用完全相同的函数怎么办?一个解决方案是在所有这些文件中重新定义该函数——但这显然违反了DRY(不要重复自己)原则。那么该如何做呢?

我注意到很多人都在问这个问题——因此有了这篇文章。您会看到,如果您只了解pytest夹具的概念,解决方案非常简单,甚至相当自然。

夹具

本文的目的不是介绍 Pytest 夹具。由于我计划写一篇专门的文章来介绍这个主题,以下将只提供一个非常简短的介绍。如果您想了解更多关于它们的信息,可以从pytest文档中学习:

关于夹具 - pytest 文档

简而言之,您可以使用夹具传递各种类型的对象,主要是数据和配置,到测试文件中。您需要在conftest.py文件中定义一个夹具,它将在该conftest.py文件的作用域内可用,即在该文件所在的同一文件夹及其所有子文件夹中的所有测试文件中。

让我们分析一个简单的例子。为了简化,您可以将以下三个文件放在同一个文件夹中:

# foo.pydeffoo(x,y):returnx+y
# conftest.pyimportpytest@pytest.fixturedefx_y_int():return20,50@pytest.fixturedefx_y_str():return"20","50"
# test_foo.pyimportfoodeftest_foo_ints(x_y_int):x,y=x_y_intassertfoo.foo(x,y)==70deftest_foo_ints(x_y_str):x,y=x_y_strassertfoo.foo(x,y)=="2050"

第一个模块,foo,定义了一个函数foo(),它接受两个参数。在conftest.py文件中,我们定义了两个夹具,x_y_intx_y_str。前者保持整数值,而后者为xy提供字符串值。

如您所见,一个夹具(fixture)是一个函数。为了在测试函数中使用它,您需要像在上述test_foo.py中那样将其作为这个测试函数的参数提供。这两个测试将会通过:

  • test_foo_ints(x_y_int): 我们使用x_y_int设置,它保持一个包含(20, 50)的元组。在解包后,我们得到x = 20y=50

  • test_foo_str(x_y_str): 我们使用x_y_str设置,它保持一个包含("20", "50")的元组。在解包后,我们得到x = "20"y="50"

这就是使用pytest设置的基本概念。你可以使用它们将任何对象传递给在定义它们的conftest.py文件作用域内的所有测试文件。

将函数传递给测试文件

我猜很多人尝试以常规 Python 方式从conftest.py文件传递辅助函数,即通过导入。让我们考虑以下一个非常简单的例子。

想象我们正在为以下模块实现单元测试:

# foomodule.pydeffootuple(x):returnx,x**2,x**3

我们希望使用以下辅助函数进行测试:

defis_tuple_fine(t:tuple)->None:msgs={"type":"It's not a tuple","length":"Length of the tuple isn't 3","values":"t[1] must be t[0]**2 and t[2] must be t[0]**3"}assertisinstance(t,tuple)isTrue,msgs["type"]assertlen(t)==3,msgs["length"]assertt[1]==t[0]**2andt[2]==t[0]**3,msgs["values"]

该函数执行三个检查(断言):

  • 一个对象(t)是一个元组

  • 它是一个包含三个元素的元组

  • 以下条件成立:t[1] == t[0]**2t[2] == t[0]**3

下面,我将讨论三种解决方案。

从 conftest 导入函数

我首先讨论这个解决方案,因为对许多人来说,这似乎是做这件事最自然的方式:通常,当我们需要从另一个模块中获取函数时,我们会导入它,不是吗?虽然这是真的,但这个解决方案只会在一个特定的场景下工作,所以我不会认为这是一个好的方法:因为它不是通用的,所以它看起来不自然。说实话,我还没有在实际情况中看到过它的使用。因此,我将讨论它,但只是为了说明它在什么时候有效。肯定的是,不要使用这种方法。

让我们创建一个包含此函数和一些其他设置的conftest.py文件:

# conftest.pyimportpytestdefis_tuple_fine(t:tuple)->None:msgs={"type":"It's not a tuple","length":"Length of the tuple isn't 3","values":"t[1] must be t[0]**2 and t[2] must be t[0]**3"}assertisinstance(t,tuple)isTrue,msgs["type"]assertlen(t)==3,msgs["length"]assertt[1]==t[0]**2andt[2]==t[0]**3,msgs["values"]@pytest.fixturedefints_1():return1@pytest.fixturedefints_2():return2

现在,测试文件:

# test_foomodule.pyimportfoomodulefromconftestimportis_tuple_finedeftest_foo(ints_1):ints=foomodule.footuple(ints_1)is_tuple_fine(ints)assertints==(1,1,1)deftest_foo_ints_2(ints_2):ints=foomodule.footuple(ints_2)is_tuple_fine(ints)assertints==(2,4,8)

这种解决方案只会在一种情况下有效:当conftest.py文件位于与测试文件相同的文件夹中时。测试将通过。

然而,当conftest.py文件保存在其他位置(目录树的上层)时——这是一个相当常见的场景——你会看到以下错误:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/466862b53c002ac6e2935e5db36feaa0.png

从 conftest.py 文件导入函数后的 Pytest 失败。作者截图

这就是为什么你不应该考虑这种方法。即使现在它可行,以后你可能需要更改测试的结构,然后测试将开始失败(如上所示)。

在测试文件中定义辅助函数

实际上,这是一个最简单的情况,在某种情况下是一个好方法:当你需要函数只在单个测试文件中可用时。那么,只需在这个特定的测试文件中定义该函数即可——不需要在conftest.py中这样做。这意味着将is_tuple_fine()的定义移动到test_foo模块。

然而,如果你需要辅助函数在多个测试文件中可用,你将不得不在其他文件中重新定义它——这种方法绝对应该避免,因为它违反了DRY(不要重复自己)原则。这时,下一个解决方案就派上用场了。

将函数作为固定装置传递

这是一个通用的解决方案,因为固定装置正是为此而创建的:将对象传递给测试文件。

如何做到这一点?让我重复一下我上面用过的句子:

你可以使用它们[固定装置]将任何对象传递给定义它们的conftest.py文件范围内的所有测试文件。

任何对象,为什么不是一个函数呢?在 Python 中,函数就像任何其他对象一样是一个对象!

这是我通常这样做的方式。我在conftest.py文件中定义了我需要的函数作为一个私有函数(因此,以下划线为前缀),即_is_tuple_fine()。然后,我定义了一个公共固定装置,其名称与函数的实际名称相同,即is_tuple_fine()。所以:

# conftest.pyimportpytestdef_is_tuple_fine(t:tuple)->None:msgs={"type":"It's not a tuple","length":"Length of the tuple isn't 3","values":"t[1] must be t[0]**2 and t[2] must be t[0]**3"}assertisinstance(t,tuple)isTrue,msgs["type"]assertlen(t)==3,msgs["length"]assertt[1]==t[0]**2andt[2]==t[0]**3,msgs["values"]@pytest.fixture(scope="session")defis_tuple_fine():return_is_tuple_fine@pytest.fixturedefints_1():return1@pytest.fixturedefints_2():return2

当实际函数非常短时,你可以在固定装置内部定义它。选择更清晰、更易读的方法。

注意"session"范围。Pytest 固定装置的默认范围是"function",这意味着固定装置在每个测试函数结束时被销毁。这个特定的固定装置可以在整个会话中工作,因此没有必要在中间销毁它。你可以在Pytest 文档中了解更多关于固定装置范围的信息。

现在,为了在测试文件中使用我们的函数,你需要像使用任何其他固定装置一样使用它:

importfoomoduledeftest_foo_ints_1(ints_1,is_tuple_fine):ints=foomodule.footuple(ints_1)is_tuple_fine(ints)assertints==(1,1,1)deftest_foo_ints_2(ints_2,is_tuple_fine):ints=foomodule.footuple(ints_2)is_tuple_fine(ints)assertints==(2,4,8)

正如你所见,你需要将is_tuple_fine固定装置作为参数传递给每个你想要使用的测试函数。然后,它将变成你需要使用的函数。

结论

当你知道第三种方法时,它看起来相当自然和地道。确实如此——因为这正是 Pytest 固定装置被设计的目的,即从conftest.py文件传递对象到测试文件。

当你不知道可以这样操作时,自然就会想到导入辅助函数。有时候它会工作,但有时候不会——你可能会花一些时间去思考发生了什么。

没有发生任何事情。这就是 Pytest 的工作方式。现在你知道该怎么做,我非常确信你不会在通过conftest.py文件传递辅助函数时遇到任何问题。有了这个工具,Pytest 测试甚至更简单。

http://www.jsqmd.com/news/373283/

相关文章:

  • Python 中的路径表示
  • 宏观经济动态规划模型与贝尔曼方程的应用
  • 干货合集:10个AI论文工具测评!本科生毕业论文写作必备指南
  • LangChain创始人:Agent 连接沙箱的两种模式(附深度架构解析)
  • 33年老协议谢幕!Win11 正式启动 NTLM 淘汰计划
  • CK5/6抗体如何揭示肺腺癌的病理特征与预后?
  • 2026年河北环卫公司推荐:基于智慧化水平实测排名,针对项目运营与环保合规痛点 - 品牌推荐
  • 科研党收藏!10个降AI率软件降AIGC网站评测对比,本科生必看!
  • 酷监控!一款高颜值的监控工具!
  • 粒子群优化
  • OpenEuler安装MiniConda
  • go语言实现http双向认证
  • 微服务架构在 C++ 和 Python中的应用
  • 【JAVA 进阶】深入探索Spring AOP:从原理到实战 - 指南
  • 2026年河北环卫公司推荐:行业先锋综合评估报告发布 - 品牌推荐
  • AI4Science开源数据汇总
  • ML.NET 作为 .NET 生态的轻量级机器学习框架,在**异常检测**(Anomaly Detection)领域提供了几类高级算法,尤其适合工控机边缘部署
  • 2026年知名的项目综合管理,项目组合管理系统,项目集管理公司用户好评名录 - 品牌鉴赏师
  • 超越PCA:设计可扩展、可解释的现代降维算法组件
  • Java小白互联网大厂面试场景:从Spring Boot到微服务架构的问答解析
  • Teamcenter用户登录失败或模块访问被拒的深度原因分析与解决
  • 2026年河北环卫公司推荐榜单:覆盖多场景服务、90%客户满意率的五强权威认证 - 品牌推荐
  • Ubuntu 24.04 设置开机自动启动命令
  • AI写论文风向标!4个热门AI论文生成工具,写论文不再是烦恼!
  • AI写论文有妙招!4款AI论文生成工具,帮你快速搞定毕业论文!
  • 2026年河北环卫公司推荐榜单:覆盖城乡一体化、智慧化转型需求的五强权威认证 - 品牌推荐
  • 《P2513 [HAOI2009] 逆序对数列》
  • 微信Linux版QVD-2026-7687漏洞深度复现:点击即执行,漏洞原理、验证方法与防御指南
  • AI写论文的绝佳选择!4款AI论文写作神器,让论文创作事半功倍!
  • 英伟达红队重磅发布:AI Agent安全实践指南,筑牢执行层安全防线