當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"],

二、相同順序的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"]
官方給出了上述代碼的依賴關系圖(左)和執行順序圖(右),

不要方,只要從測驗函式test_order開始,一層一層跟著fixture的依賴一層一層梳理下去就對上了,
到這里,其實也就能更進一步理解了,如果想控制好執行順序,就要給這些請求依賴提供足夠的資訊,
這樣pytest能夠找出一個清晰的線性依賴鏈,最終給呼叫它們的測驗函式一個確定的操作順序,
2.請求依賴不呈線性的情況,會影響操作執行
此外,如果存在歧義,出現多種執行順序,那pytest可以在多種順序里任選,
基于上述的請求依賴關系圖(左),假設d沒有請求c,那么此時的依賴關系就變成了右圖所示:

可以看出:
- 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,
它們的依賴關系圖變成了這樣:

因為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還是在這個類里運行了,

但是,僅僅是一個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'])
運行都是可以通過的,這里的依賴關系圖是這樣的,

在類TestClassWithAutouse中:
test_req和test_no_req是2個測驗方法,c3是autouse,并且請求了c2,所以c2也成了autouse,雖然2個測驗并沒去請求c2和c3,但是都執行了,而且在c1之前執行,
在這里,c3請求了還order,同樣地,在c3的作用域內,order也扮演了autouse的存在,但是在TestClassWithoutAutouse,order就不是
autouse了,所以類TestClassWithoutAutouse中的test_no_req可以運行成功,因為order=[],
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280171.html
標籤:其他
上一篇:PHP反序列化入門
