前戲
fixture是在測試函數運行前后,由pytest執行的外殼函數。fixture中的代碼可以定制,滿足多變的測試需求,包括定義傳入測試中的數據集、配置測試前系統的初始狀態、為批量測試提供數據源等等。
下面是一個簡單的fixture
import pytest @pytest.fixture() def some_data(): return 37 def test_some_data(some_data): assert some_data == 37
我們來運行一下
@pytest.fixture()裝飾器用於聲明函數是一個fixture,如果測試函數的參數列表中包含fixture名,那么pytest會檢測到,並在測試函數運行之前執行該fixture。fixture可以完成任務,也可以返回數據給測試函數。
測試用例test_some_data()的參數列表中包含一個fixture名some_data,pytest會以該名稱搜索fixture。pytest會優先搜索該測試所在的模塊,然后搜索conftest.py
建議:被fixture裝飾的函數別以test命名,比如上面的命名為some_data,而不是test_data。當然,這樣命名也沒有錯,只是為了方便區分是fixture還是測試用例(函數)
conftest.py
fixture可以放在單獨的測試文件里,如果你希望多個測試文件共享fixture,可以在某個公共目錄下新建一個conftest.py文件,將fixture放在其中。
如果你希望fixture的作用域僅限於某個測試文件,那么將它寫在該測試文件里。可以供所在目錄及其子目錄下的測試使用。
說明:
盡管conftest.py是python模塊,但它不能被當測試文件導入,import conftest的用法是不允許出現的。
我們將上面的代碼更改
conftest.py
import pytest @pytest.fixture() def some_data(): return 37
test_fixture.py
def test_some_data(some_data): assert some_data == 37
當pytest執行test_some_data函數時,會自動去conftest.py里找some_data並執行,這樣比如我們的用例,有些需要登錄,有些不需要登錄,我們可以給需要登錄的用例加上fixture,當然,fixture也可以自定義使用范圍,是函數,還是類,或者一個模塊,后面會介紹。
fixture配置及銷毀yield
fixture會在測試函數之前運行,但如果fixture函數包含yield,那么系統會在yield處停止,轉而執行測試函數,等測試函數執行完畢后再回到fixture,繼續執行yield后面的代碼。因此,可以將yield之前的代碼視為配置(setup)過程,將yield之后的代碼視為清理(teardown)過程,無論測試過程中發生了什么,yield之后的代碼都會被執行。
將上面的conftest.py里的代碼修改一下
import pytest @pytest.fixture() def some_data(): yield 37 print('測試結束')
--setup-show
上面的測試,我們不會看到fixture的執行過程。如果希望看到測試過程中執行的是什么,以及執行的先后順序,pytest提供的--setup-show選項可以實現這個功能
我們的測試被夾在中間,pytest將每一個fixture的執行分成SETUP和TEARDOWN兩部分。
fixture名稱前面的F代表的是fixture的作用范圍,F代表函數級別的作用范圍,如果是S,則代表的是會話級別的作用范圍。
指定fixture作用范圍
fixture包含一個叫scope(作用范圍)的可選參數,用於控制fixture執行配置和銷毀邏輯的頻率。@pytest.fixture()的scope參數有四個帶選值:function,class,module,session(默認值為function)。前面用到的fixture都沒有指定scope,因此他們的作用范圍都是默認的函數級別。
scope=“function”
函數級別的fixture每個測試函數只需要運行一次,配置代碼在測試用例運行之前運行,銷毀代碼在測試用例運行之后運行。function是scope參數的默認值
conftest.py
import pytest @pytest.fixture(scope="function") # 函數級別的 def login(): print('登錄成功')
test_fixture.py
def test_index(login): print('訪問index') def test_home(login): print('訪問home')
執行結果可以看到,我們在執行每個函數之前都執行了fixture
scope=“class”
類級別的fixture每個測試類只需要運行一次,無論測試類里有多少類方法都可以共享這個fixture
conftest.py
import pytest @pytest.fixture(scope="class") # 類級別 def login(): print('登錄成功')
test_fixture.py
class Test1(): def test_index(self,login): print('訪問index1') def test_home(self,login): print('訪問home1') class Test2(): def test_index(self,login): print('訪問index2') def test_home(self,login): print('訪問home2')
scope=“module”
模塊級別的fixture,每個模塊只需要運行一次,無論模塊里有多少個測試函數、類方法或者其他fixture都可以共享這個fixture
只需要修改conftest.py
import pytest @pytest.fixture(scope="module") # 模塊級別 def login(): print('登錄成功')
scope=“session”
會話級別的fixture,每次會話只需要運行一次,所有測試函數、方法都可以共享這個fixture
我們把上面的test_fixture.py在復制一份,改名為test_fixture2,這樣我們在testpytest下就有兩個test文件了
如果有兩個文件,把session改為module,則會執行兩次,因為有兩個py文件
使用usefixtures指定fixture
目前為止用到fixture的測試,都是在測試函數的參數列表中指定fixture,實際上,也可以用@pytest.mark.usefixtures('fixture1', 'fixture2')標記測試函數或類。使用usefixtures,需要在參數列表中指定一個或多個fixture字符串。
使用usefixture和在測試方法中添加fixture參數,二者本體上是差不多的,區別之一在與后者才能使用fixture的返回值

import pytest @pytest.fixture(scope="function") def login(): print('登錄成功')

import pytest @pytest.mark.usefixtures('login') def test_index(): print('訪問index1') def test_home(): print('訪問home1')
如果是類值需要在類名上面加上fixture

import pytest @pytest.mark.usefixtures('login') class Test1(): def test_index(self): print('訪問index1') def test_home(self): print('訪問home1') @pytest.mark.usefixtures('login') class Test2(): def test_index(self): print('訪問index2') def test_home(self): print('訪問home2')
@pytest.mark.usefixtures("cleandir", "anotherfixture")指定多個fixture
autouse
不想源測試數據有任何改動,或全部都實現自動應用, 沒特例,也都不需要返回值時可以選擇自動應用
使用fixture中參數autouse=True實現

import pytest @pytest.fixture(autouse=True) def login(): print('登錄成功')

class Test1(): def test_index(self): print('訪問index1') def test_home(self): print('訪問home1') class Test2(): def test_index(self): print('訪問index2') def test_home(self): print('訪問home2')

def test_index(): print('訪問index1') def test_home(): print('訪問home1')
可以看到,我們加了autouse=True之后,會給所有的方法都應用,不用再每個方法都使用fixture
如果級別是class的話,則有class的只會執行一次
為fixture重命名
fixture的名字展示在使用它的測試或其他fixture函數的參數列表上,通常會和fixture函數名保持一致,但pytest允許使用@pytest.fixture()的name參數對fixture重命名
conftest.py
import pytest @pytest.fixture(name='login') # 重命名為login def alibaba_taobao_app_login(): print('登錄成功')
test_fixture2.py
def test_index(login): print('訪問index1') def test_home(login): print('訪問home1')
這個fixture原來的名字是 alibaba_taobao_app_login ,重命名后變成了login
查看fixture
我們可以使用--fixtures命令行選項,並提供所在測試文件名。pytest將列舉所有可供測試使用的fixture,包括重命名的。我們自己重新定義的會出現在底部