【pytest官方文档】解读fixtures - 11. fixture的执行顺序,3要素详解(长文预警)

简介: 【pytest官方文档】解读fixtures - 11. fixture的执行顺序,3要素详解(长文预警)

当pytest要执行一个测试函数,这个测试函数还请求了fixture函数,那么这时候pytest就要先确定fixture的执行顺序了。


影响因素有三:


  • scope,就是fixture函数的作用范围,比如scope='class'
  • dependencies,可能会存在fixture请求了别的fixture,所以产生了依赖关系,也要考虑进去。
  • autouse,如果autouse=True,那么在作用范围内,这个fixture是最先调用的。


所以,像fixture函数或测试函数的名称、定义的位置、定义的顺序以及请求fixture的顺序,除了巧合之外,对执行顺序没有任何影响。


对于这些巧合情况,虽然pytest会尽力保持每次运行的顺序都一样,但是也难免会有意外。所以,如果我们想控制好顺序,最安全的方法还是

依赖上述三点,并且要弄清依赖关系。


一、使用范围更大的fixture函数优先执行


更大范围(比如session)的fixture会在小范围(比如函数或类)之前执行。


代码示例:


import pytest
@pytest.fixture(scope="session")
def order():
    return []
@pytest.fixture
def func(order):
    order.append("function")
@pytest.fixture(scope="class")
def cls(order):
    order.append("class")
@pytest.fixture(scope="module")
def mod(order):
    order.append("module")
@pytest.fixture(scope="package")
def pack(order):
    order.append("package")
@pytest.fixture(scope="session")
def sess(order):
    order.append("session")
class TestClass:
    def test_order(self, func, cls, mod, pack, sess, order):
        assert order == ["session", "package", "module", "class", "function"]


运行结果:


test_module1.py .                                                        [100%]
============================== 1 passed in 0.01s ==============================
Process finished with exit code 0


既然运行通过,那么这些fixture函数的运行顺序就是列表里的顺序["session", "package", "module", "class", "function"]


1268169-20210424231949217-1611563757.png


二、相同顺序的fixture基于依赖项执行


当一个fixture函数请另一个fixture函数,另一个会先执行。


比如,fixturea请求fixtureb,需要用b返回的结果。那么b先执行,因为a依赖于b,必须得让b先执行,否则a就没法干活。


另外,即使a不需要用b返回的结果,只要a需要确保在b之后执行,a仍然可以通过请求b来控制顺序。


1.请求依赖呈线性情况下


代码示例:


import pytest
@pytest.fixture
def order():
    return []
@pytest.fixture
def a(order):
    order.append("a")
@pytest.fixture
def b(a, order):
    order.append("b")
@pytest.fixture
def c(a, b, order):
    order.append("c")
@pytest.fixture
def d(c, b, order):
    order.append("d")
@pytest.fixture
def e(d, b, order):
    order.append("e")
@pytest.fixture
def f(e, order):
    order.append("f")
@pytest.fixture
def g(f, c, order):
    order.append("g")
def test_order(g, order):
    assert order == ["a", "b", "c", "d", "e", "f", "g"]


官方给出了上述代码的依赖关系图(左)和执行顺序图(右)。


1268169-20210424234502726-1717065626.png


不要方,只要从测试函数test_order开始,一层一层跟着fixture的依赖一层一层梳理下去就对上了。


到这里,其实也就能更进一步理解了,如果想控制好执行顺序,就要给这些请求依赖提供足够的信息。


这样pytest能够找出一个清晰的线性依赖链,最终给调用它们的测试函数一个确定的操作顺序。


2.请求依赖不呈线性的情况,会影响操作执行


此外,如果存在歧义,出现多种执行顺序,那pytest可以在多种顺序里任选。

基于上述的请求依赖关系图(左),假设d没有请求c,那么此时的依赖关系就变成了右图所示:


1268169-20210425213417241-601322526.png


可以看出:


  • c此时只被一个g请求。
  • g既请求了c,还请求了f。


因为c现在除了一个g,其他没有别的依赖关系,所以现在pytest不知道c应该是在f,e之前执行,还是应该在d之后执行。


这时候,pytest就会认定,c可以在g和b之间的任何位置点执行。也就是说,c必须在b之后和g之前执行。


如果这种情况出现,那么你预期的测试行为或者测试结果可能会受到影响。可以修改下代码,让d中没有请求c,并且我加了print,方便

看fixture的运行顺序。


运行下代码:


test_module1.py 
运行order
运行a
运行b
运行d
运行e
运行f
运行c
运行g
F
demo\test_module1.py:51 (test_order)
['a', 'b', 'd...'f', 'c', ...] != ['a', 'b', 'c...'e', 'f', ...]
Expected :['a', 'b', 'c...'e', 'f', ...]
Actual   :['a', 'b', 'd...'f', 'c', ...]


会看到测试失败了,因为fixture的执行顺序变了,导致添加到order列表的元素顺序也变了,实际与预期结果不相等,测试失败。


不过可以从打印出的fixture运行顺序看出,c确实在b之后和g之前运行了。


官方描述这些想要表达的什么呢?


我觉得应该是这个,如果你希望精确控制执行顺序,避免顺序不对而造成执行操作或测试结果有误,那么就要给足请求依赖,好让pytest

线性制定执行顺序。


三、Autouse的fixtures,会优先执行


1. autouse的妙用


如果请求了一个autouse=True的fixture函数,那么这个autouse的fixture函数会比请求的其他fixture都要先执行。


另外,如果fixture a是autouse的,而fixture b不是。而fixture a又请求fixture b,那么fixture b也将变成autouse的fixture ,但仅适用于请求了a的测试。


其实这点也很好理解,既然a是要先执行的,a又请求了b,说明a依赖于b,那么b自然也是要先于a执行的。


在上一个例子中,由于d没有去请求c,导致依赖关系模糊,最后影响了执行结果。


但是如果c是autouse,那么b和a也就自动变成了autouse,因为c依赖于b和a。所以,这时候,c,b,a都会在其他非autouse的fixture函数之前执行。


修改下代码,在c上加上autouse:


import pytest
@pytest.fixture
def order():
    print("\n运行order")
    return []
@pytest.fixture
def a(order):
    print("运行a")
    order.append("a")
@pytest.fixture
def b(a, order):
    print("运行b")
    order.append("b")
@pytest.fixture(autouse=True)
def c(a, b, order):
    print("运行c")
    order.append("c")
@pytest.fixture
def d(b, order):
    print("运行d")
    order.append("d")
@pytest.fixture
def e(d, b, order):
    print("运行e")
    order.append("e")
@pytest.fixture
def f(e, order):
    print("运行f")
    order.append("f")
@pytest.fixture
def g(f, c, order):
    print("运行g")
    order.append("g")
def test_order(g, order):
    assert order == ["a", "b", "c", "d", "e", "f", "g"]


运行结果:


test_module1.py 
运行order
运行a
运行b
运行c
运行d
运行e
运行f
运行g
.                                                        [100%]
============================== 1 passed in 0.01s ==============================
Process finished with exit code 0


执行顺序正常了,从a到g。


它们的依赖关系图变成了这样:


1268169-20210425221318672-334716440.png


因为c变成了autouse,所以在图里处于d之上的位置,这时候pytest又可以将执行顺序线性化了。而且,c也让b和a都变成了autouse的fixture。


2. autouse的慎用


在使用autouse的时候也要小心。


因为一个测试函数即使没有直接请求一个autouse fixture,但是只要这个测试函数在这个autouse的作用范围内,那么这个autouse就会自动执行。


看代码示例:


import pytest
@pytest.fixture(scope="class")
def order():
    return []
@pytest.fixture(scope="class", autouse=True)
def c1(order):
    order.append("c1")
@pytest.fixture(scope="class")
def c2(order):
    order.append("c2")
@pytest.fixture(scope="class")
def c3(order, c1):
    order.append("c3")
class TestClassWithC1Request:
    def test_order(self, order, c1, c3):
        assert order == ["c1", "c3"]
class TestClassWithoutC1Request:
    def test_order(self, order, c2):
        assert order == ["c1", "c2"]


执行代码,运行case是通过的,说明order == ["c1", "c2"]


可以看到,虽然类TestClassWithoutC1Request(官方写的是TestClassWithC1Request,应该是错了)没有请求c1,但是c1还是在这个类里运行了。


1268169-20210425222713837-1233802893.png


但是,仅仅是一个autouse的fixture请求了一个非autouse的话,其实这并不能说这个非autouse的fixture也成为了一个可以应用到上下文的fixture函数。


仅仅是适用于请求它的那个autouse fixture的作用范围。


例如,看下面代码:


import pytest
@pytest.fixture
def order():
    return []
@pytest.fixture
def c1(order):
    order.append("c1")
@pytest.fixture
def c2(order):
    order.append("c2")
class TestClassWithAutouse:
    @pytest.fixture(autouse=True)
    def c3(self, order, c2):
        order.append("c3")
    def test_req(self, order, c1):
        assert order == ["c2", "c3", "c1"]
    def test_no_req(self, order):
        assert order == ["c2", "c3"]
class TestClassWithoutAutouse:
    def test_req(self, order, c1):
        assert order == ["c1"]
    def test_no_req(self, order):
        assert order == []
if __name__ == '__main__':
    pytest.main(['-s', 'test_module2.py'])


运行都是可以通过的,这里的依赖关系图是这样的。


1268169-20210425225348900-1503508186.png


在类TestClassWithAutouse中:


  • test_reqtest_no_req是2个测试方法。
  • c3是autouse,并且请求了c2,所以c2也成了autouse。虽然2个测试并没去请求c2c3,但是都执行了,而且在c1之前执行。


在这里,c3请求了还order,同样地,在c3的作用域内,order也扮演了autouse的存在。但是在TestClassWithoutAutouse,order就不是

autouse了,所以类TestClassWithoutAutouse中的test_no_req可以运行成功,因为order=[]

相关文章
|
存储 缓存 NoSQL
MySQL索引详解(一文搞懂)
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。
50114 17
MySQL索引详解(一文搞懂)
|
2月前
|
安全 数据安全/隐私保护 Python
Python实现RSA加解密
本文介绍如何使用Python的PyCryptodome库实现RSA加解密。内容包括安装依赖、生成密钥对、保存公私钥文件,以及封装加密、解密和公钥导出功能的类,并提供完整调用示例,适用于安全通信开发场景。(238字)
117 0
|
6月前
|
机器学习/深度学习 人工智能 算法
AI 基础知识从 0.6 到 0.7—— 彻底拆解深度神经网络训练的五大核心步骤
本文以一个经典的PyTorch手写数字识别代码示例为引子,深入剖析了简洁代码背后隐藏的深度神经网络(DNN)训练全过程。
1110 56
|
NoSQL 索引
MongoDB查询优化:从 10s 到 10ms
本文是我前同事付秋雷最近遇到到一个关于MongoDB执行计划选择的问题,非常有意思,在探索源码之后,他将整个问题搞明白并整理分享出来。付秋雷(他的博客)曾是Tair(阿里内部用得非常官方的KV存储系统)的核心开发,目前就职于蘑菇街。
|
7月前
|
存储 缓存 人工智能
好奇心之旅:Cursor代码库索引机制的学习笔记
本文介绍了Cursor等AI编程工具中codebase indexing的技术原理,包括Merkle Tree与向量数据库的应用,以及开源方案Continue的启发。作者通过探索这些工具背后的机制,提升了对AI编程和代码索引技术的理解,也为进一步学习提供了思路。
好奇心之旅:Cursor代码库索引机制的学习笔记
|
8月前
|
人工智能 数据可视化 Devops
敏捷VS瀑布?敏捷项目管理方法论全解析:从核心逻辑到工具适配
本文深入解析了四大主流敏捷项目管理方法论:Scrum、Kanban、Lean和SAFe,分别阐述其核心逻辑、适用场景及典型工具。Scrum适合需求多变的中小型项目,强调迭代开发;Kanban通过可视化优化流程,适用于非迭代任务;Lean聚焦价值流优化,适于效率提升场景;SAFe为大型组织提供分层协作框架。文章对比各方法的特点与工具需求,并展望AI技术对敏捷管理的革新,为企业选择适配的方法与工具提供了理论与实践指导。
|
敏捷开发 数据可视化 数据挖掘
任务看板是什么?如何选择合适的任务看板工具?
任务看板是一种可视化的项目管理工具,通过卡片和列的形式展示任务状态,帮助团队成员清晰了解项目进展,提高工作效率和协作能力。本文介绍了任务看板的特点、选择要点及三款推荐工具:板栗看板、Monday.com 和 Jira,分别从功能、协作、可视化、价格和用户体验等方面进行了对比。
任务看板是什么?如何选择合适的任务看板工具?
|
存储 监控 NoSQL
【MongoDB 专栏】MongoDB 在实时数据分析中的应用
【5月更文挑战第11天】MongoDB,作为强大的非关系型数据库,擅长实时数据分析。其灵活数据模型适应多样化数据,分布式架构支持水平扩展,处理海量数据和高并发查询。应用于物联网、实时监控、金融交易分析及电商个性化推荐等领域。结合流处理技术和数据可视化工具,提升实时分析效能。然而,注意数据一致性和性能调优是应用关键。未来,MongoDB将持续发展,为企业实时数据分析带来更多可能性和机遇。
551 1
【MongoDB 专栏】MongoDB 在实时数据分析中的应用