pytest fixtures的执行顺序是什么?

61
为了测试一个应用程序,我想创建一个“autouse=True”fixture来monkeypatch“smtplib.SMTP.connect”,以便在测试案例意外发送电子邮件时使测试失败。
然而,在我确实期望测试发送电子邮件的情况下,我希望使用另一个fixture来记录这些电子邮件(很可能是通过使用“pytest-localserver”的“smtpserver”fixture,并monkeypatching“connect”方法来使用该fixture返回的主机/端口)。
当然,这只能在执行其他fixture之前执行autouse fixture(作为funcarg加载)。fixture是否有特定的执行顺序和/或是否有保证执行顺序的方法?

4
非常有效的问题,我曾多次看到夹具被滥用,其中最棘手的问题之一是哪个夹具在哪个夹具之前运行。 - Fruch
1
另一个有用的技巧是,fixture 可以检查 pytest 标记的测试函数。这意味着您可以对需要执行特殊操作的测试进行 @mark,然后使用 request 对象检查测试函数是否具有该标记。如果有,则在 fixture 中执行不同的操作。 - user2859458
2
也知道拆卸顺序会很好。 - 4xy
5个回答

58

控制夹具执行顺序的最简单方法是在后续夹具中请求先前的夹具。因此,要确保 ba 之前运行:

@pytest.fixture(autouse=True, scope="function")
def b():
    pass

@pytest.fixture(scope="function")
def a(b):
    pass

有关通用装置解析顺序的详细信息,请参阅下面Maxim的优秀答案或查看文档


似乎在pytest 3.0.2上无法正常工作(没有自动使用)。尝试使用另一个创建将sys.stdout保存到变量的类的fixture与capfd一起使用:避免fixture捕获输出,尝试强制capfd首先设置如此并不起作用。 - Sam Brightman
pytest 3.0.2和“no autouse”是什么意思?这段代码在pytest 3.0.2和3.0.3(最新版本)中对我来说都很好用。 - Barry
大多数情况下,我看到这个顺序失败是因为我的某个固定装置由于错误而未能完成,请仔细查找设置错误。 - Manel Clos

26

TL;DR

构建装置评估顺序时需考虑以下3个方面,按优先级排序:

  • 装置依赖性 - 装置从最根本需要的开始评估,到在测试函数中所需的装置。
  • 装置作用域 - 装置从作用域session通过modulefunction进行评估。在相同作用域内,autouse装置将在非autouse装置之前评估。
  • 装置在测试函数参数中的位置 - 装置按照测试函数参数中出现的顺序进行评估,从左边右边

官方说明及代码示例请查看下面链接

https://docs.pytest.org/en/stable/fixture.html#fixture-instantiation-order

UPDATE

当前文档提到有3个因素作为唯一考虑因素,同时不鼓励依赖其他因素(例如测试函数参数中的顺序):

  1. 作用域
  2. 依赖性
  3. autouse

上面的链接已经过时。请查看更新的链接


4
第三点“从最左边到最右边的出现顺序”在pytest文档中没有得到证实。根据 https://docs.pytest.org/en/stable/fixture.html#fixture-order 中的内容:“...夹杂在执行顺序中的夹具请求顺序并不影响执行顺序,除了偶然情况”。 - convoliution
3
链接中仅提到了3种情况:1-作用域 2-依赖项 3-自动使用。注意:“测试函数参数中的Fixture位置”不在列表中。不要依赖它。 - jfs
1
谢谢大家指出,我已经更新了答案。 - Maxim
我正在尝试确定这个位置参数从左到右的顺序是否总是可以用fixture依赖等效表示(文档部分fixtures-of-the-same-order-execute-based-on-dependencies)。我在思考是否有一种情况,其中一个测试用例需要按照某种顺序进行,而另一个测试用例需要与第一个不同的顺序。我们可以随意定义fixture的依赖关系,即使该fixture在父fixture的主体中未被使用,也能保持顺序。我们能想象出两个测试用例在它们的排序需求上发生冲突的场景吗? - jxramos
其实也许我的说法太笼统了,我们可以以任何方式定义装置之间的依赖关系,除了可能是循环依赖?我以前从来没有真正尝试过这样做。 - jxramos

12

我刚遇到了一个问题,涉及两个函数作用域的自动用法固件。我希望夹具b在夹具a之前运行,但每次a都会先运行。我想可能是按字母顺序排列,所以我将a重命名为c,现在b首先运行。Pytest似乎没有记录这个。这只是一个幸运的猜测。:-)

对于自动使用的固件来说是这样。考虑到更广泛的范围(例如modulesession),当pytest遇到需要它的测试时,夹具才会执行。因此,如果有两个测试,第一个测试使用名为sb而不是名为sasession作用域夹具,则会首先执行sb。当下一个测试运行时,它将启动sa,假设它需要sa


1
可以确认适用于 py.test 版本 2.6.4。 - Nitrodist
我也在使用 2.6.4。 - ebeezer
我认为最好删除这个答案,因为它让人们通过使用字母顺序的“功能”来“节省时间”。这真的很糟糕。 - Gulzar
3
这个答案帮助我理解了我不能正确地强制执行某些特定的装置顺序。而且我不想使用字母顺序,所以我试图重写我的装置,使它们不依赖于顺序。总之,我仍然觉得这很有帮助 :) - ololobus
Pytest文档指出,这种行为“除了巧合之外,对执行顺序没有任何影响。虽然pytest会尝试确保这些巧合在每次运行时保持一致,但这不是应该依赖的东西。” - Maxim

2
据我所知,您可以依赖于更高级别的固定装置首先执行。因此,如果您创建了一个会话范围的自动使用固定装置来猴子补丁 smtplib.SMTP.connect,那么您可以创建一个函数范围的固定装置,该固定装置为一个测试撤消此猴子补丁,在此之后恢复它。我认为最简单的方法是创建您自己的 smtpserver 固定装置,该装置取决于 disallow_smtp 固定装置以及来自 pytest-localserversmtpserver 固定装置,并处理使这两个装置一起工作所需的所有设置和拆卸。
顺便说一下,这是 pytest-django 如何处理其数据库访问的方式,您可以尝试查看代码,但它远非简单的示例,并且具有许多自己的奇怪之处。

-1
通过以下代码的帮助,我们可以轻松设置夹具/函数的执行顺序。

e.g:-

执行顺序优先

@pytest.mark.order(1)

执行顺序第二

@pytest.mark.order(2)

8
这个解决方案可能是指一个名为 pytest-ordering 的外部 pytest 插件,而不是 pytest 本身。 - gbonetti
我曾见过这个用于测试案例/测试函数,那么这个插件是否也能管理其他插件呢? - jxramos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接