當pytest要執行一個測試函數,這個測試函數還請求了fixture函數,那麼這時候pytest就要先确定fixture的執行順序了。
影響因素有三:
-
,就是fixture函數的作用範圍,比如scope
。scope='class'
-
,可能會存在fixture請求了别的fixture,是以産生了依賴關系,也要考慮進去。dependencies
-
,如果autouse
,那麼在作用範圍内,這個fixture是最先調用的。autouse=True
一、使用範圍更大的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函數,另一個會先執行。
比如,fixture
a
請求fixture
b
,需要用b傳回的結果。那麼b先執行,因為a依賴于b,必須得讓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"]
官方給出了上述代碼的依賴關系圖(左)和執行順序圖(右)。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM5gDNyI2M2cjY4QWZiljMzYzX2MzMyEjMwIzLclDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
不要方,隻要從測試函數
test_order
開始,一層一層跟着fixture的依賴梳理下去就對上了。到這裡,其實也就能更進一步了解了,如果想控制好執行順序,就要給這些請求依賴提供足夠的資訊。
這樣pytest能夠找出一個清晰的線性依賴鍊,最終給調用它們的測試函數一個确定的操作順序。
2.請求依賴不呈線性的情況,會影響操作執行
此外,如果存在歧義,出現多種執行順序,那pytest可以在多種順序裡任選。
基于上述的請求依賴關系圖(左),假設d沒有請求c,那麼此時的依賴關系就變成了右圖所示:
這時候,pytest就會認定,c可以在g和b之間的任何位置點執行。也就是說,有多種執行順序的選擇。
運作下代碼:
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又請求了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"]