目錄
- 1. fixture的聲明
- 2. fixture的調用
-
- 2.1 fixture的調用方式
-
- 2.1.1 使用fixturename
- 2.1.2 使用`@pytest.mark.usefixtures("fixturename")`
- 2.1.3 autouse——自動應用
- 2.2 fixture使用的靈活性
-
- 2.2.1 一個fixture函數可以調用其他的fixture
- 2.2.2 fixture函數可被反複重用
- 2.2.3 測試函數/fixture函數可以一次調用多個fixture
- 2.2.4 同一測試執行期間,fixture可被多次請求
1. fixture的聲明
我們使用
@pytest.fixture()
來聲明fixture函數。
fixture()
即可無參數進行聲明,也可以帶參數聲明。
示例1:
-
無參數進行聲明@pytest.fixture()
import pytest
@pytest.fixture #fixture()未帶任何參數,聲明一個fixture函數
def fixture_demo():
print("這個是一個fixture的demo示範")
def test_demo(fixture_demo): #調用fixture函數——fixture_demo
print("這是一個測試demo。")
-
@pytest.fixture()
有參數的進行聲明
通過【pytest】2. pytest中的fixture (1) : fixture和fixture API —@pytest.fixture()的簡單說明中的說明,我們對
的參數有了一定了解。fixture()
可以帶着這些參數去聲明一個fixture函數。fixture()
import pytest
@pytest.fixture(params=[1,2,3]) #fixture()帶着parmas對ids()進行fixture函數的聲明
def ids(request):
data=request.param
print(f'擷取測試資料{data}')
return data
def test_ids(ids): #調用fixture函數-ids()
print(ids)
2. fixture的調用
2.1 fixture的調用方式
fixture有三種調用方式,分别為:
1. 使用 fixturename
2. 使用@pytest.mark.usefixtures(“fixturename”)
3. autouse——自動應用
2.1.1 使用fixturename
通過【pytest】2. pytest中的fixture (1) : fixture和fixture API —@pytest.fixture()的簡單說明中對
@fixture()
參數的介紹,我們知道fixturename預設是
@pytest.fixture()
所裝飾的函數的函數名,如果傳入
name
參數,則fixturename就是
name
傳入的内容。
當要調用fixture的時候,隻要将fixturename作為參數傳入測試函數即可。
示例2:
1.使用被裝飾的函數的函數名
import pytest
@pytest.fixture() #未傳入name,是以fixturename為函數名login
def login():
print('login')
def test_case1(login): #将fixturename作為參數傳入
print('這是testcase1')
2.使用fixture别名
import pytest
@pytest.fixture(name='login1') #傳入name,是以fixturename為login1
def login():
print('login')
def test_case1(login1): #将fixturename作為參數傳入
print('這是testcase1')
注意:
當使用fixture的别名後,被裝束函數的函數名失效,無法再繼續使用其進行fixture的調用。
2.1.2 使用 @pytest.mark.usefixtures("fixturename")
@pytest.mark.usefixtures("fixturename")
根據pytest官方文檔的介紹,如果想讓某個fixture适用于類、子產品和項目中所有測試函數的話,會使用
usefixtures
來調用fixture。當然,單個測試函數也可以使用此方法來調用fixture。
使用
usefixtures
調用fixture和直接使用
fixturename
來調用fixture是有差別的,可總結為:
- 使用
調用的fixture隻能用于配置測試前系統的初始狀态,無法為測試用例提供測試資料;但使用usefixtures
調用的fixture卻可以實作這兩個功能。fixturename
示例:
1.聲明一個名為
的fixture,并通過
login
傳入測試資料集。test_case1使用
fixture()
的方法調用
usefixtures
,并且在函數内部想要列印login的傳回值。
login
運作後發現,結果和我們預期的不一樣,import pytest @pytest.fixture(params=[1,2,3]) # 傳入測試資料集 def login(request): print("登陸操作") return request.param #傳回測試資料 class TestClass1: @pytest.mark.usefixtures('login') #使用usefixtures調用login def test_case1(self): print("這是Testclass1中testcase1") print(login) #列印login
列印出的是函數對象資訊,不是傳回值。 2.修改剛剛的代碼,使用fixturename來調用
print(login)
login
運作後我們可以發現,結果和我們預期的一緻,把login的傳回值成功列印出來。import pytest @pytest.fixture(params=[1,2,3]) # 傳入測試資料集 def login(request): print("登陸操作") return request.param #傳回測試資料 class TestClass1: #@pytest.mark.usefixtures('login') #使用usefixtures調用login def test_case1(self,login): #使用fixturename的方式調用login print("這是Testclass1中testcase1") print(login) #列印login
-
調用fixture的方法隻适用于測試函數,對于fixture函數不生效;使用usefixtures
調用fixture的方法對測試函數和fixture函數都适用。fixturename
示例:
1.以下demo想要實作執行測試用例前列印“登陸操作”,用例執行結束後列印“登出操作”。聲明
和
login
為fixture函數,并且讓
logout
使用
logout
的方法調用
usefixtures
,再讓
login
調用
test_case1
。
logout
通過運作結果我們發現,結果隻在執行測試函用例後列印了“登出操作”,與我們預期的結果不一緻。 2.修改上面的demo代碼,讓import pytest @pytest.fixture() def login(): print("登陸操作") @pytest.mark.usefixtures('login') @pytest.fixture() def logout(): yield print("登出操作") class TestClass1: def test_case1(self,logout): print("這是Testclass1中testcase1")
使用
logout
的方式調用
fixturename
。
login
運作後我們發現,結果與我們預期的一緻。 由此可以看出來,import pytest @pytest.fixture() def login(): print("登陸操作") #@pytest.mark.usefixtures('login') @pytest.fixture() def logout(login): #使用fixturename的方式調用login yield print("登出操作") class TestClass1: def test_case1(self,logout): print("這是Testclass1中testcase1")
的調用方法對于fixture函數無效。
userfixtures
下面我們将通過示例來示範userfixtures的使用方法:
為了示範效果,我們建立一個包,并在裡面建立一個conftest.py。用于定義fixture函數。關于conftest.py的相關内容,後面會單獨寫一遍文章詳細介紹。
在conftest.py中聲明的一個
login
函數。
import pytest
@pytest.fixture()
def login():
print('登入操作')
1.整個類中應用fixture
使用
usefixtures
,讓
TestClass1
這個類中所有測試函數都調用
login
。
import pytest
@pytest.mark.usefixtures('login') #讓TestClass1調用login
class TestClass1:
def test_case1(self):
print("這是Testclass1中testcase1")
def test_case2(self):
print('這是Testclass1中testcase2')
class TestClass2:
def test_case3(self):
print('這是Testclass2中的testcase3')
運作結果:
通過結果我們可以發現,隻有TestClass1中的test_case1和test_case2調用了login。
2.整個子產品應用fixture
根據官方文檔說明,在整個子產品中應用fixture,可在子產品使用
pytestmark=pytest.mark.usefixtures("fixturename")
。
修改測試代碼如下:
import pytest
pytestmark = pytest.mark.usefixtures("login") #使用pytestmark在子產品中使用usefixtures
class TestClass1:
def test_case1(self):
print("這是Testclass1中testcase1")
def test_case2(self):
print('這是Testclass1中testcase2')
class TestClass2:
def test_case3(self):
print('這是Testclass2中的testcase3')
運作結果:
通過運作結果可發現,整個子產品中,所有測試類裡面的測試函數都調用了login。
3.整個項目中使用
在
pytest.ini
中配置
usefixtures
。
示範項目結構如圖:
對應檔案中的代碼如下:
test_demo
中的
test_demo.py
:
class TestClass1:
def test_case1(self):
print("這是pytest_demo包中test_demo子產品裡Testclass1中testcase1")
class TestClass2:
def test_case2(self):
print('這是pytest_demo包中test_demo子產品裡Testclass2中的testcase2')
test_usefixtures
中的
test_usefixtures.py
:
class TestClass1:
def test_case1(self):
print("這是pytest_usefixtures包中test_usefixtures子產品裡Testclass1中testcase1")
class TestClass2:
def test_case2(self):
print('這是pytest_usefixtures包中test_usefixtures子產品裡Testclass2中的testcase2')
TestDemo根目錄
下的
conftest.py
:
import pytest
@pytest.fixture()
def login():
print('登入操作')
TestDemo根目錄
下的
pytest.ini
:
[pytest]
usefixtures = login
運作整個項目:
項目中的測試函數都調用了conftest.py中的login。
4.使用usefixtures調用多個fixture
我們可以使用
@pytest.mark.usefixtures('fixturename1','fixturename2')
來調用多個fixture。
conftest.py
中新增一個fixture:
import pytest
@pytest.fixture()
def login():
print('登入操作')
@pytest.fixture()
def printids():
print("列印ids。")
修改
test_usefixtures.py
:
@pytest.mark.usefixtures('login','printids')
class TestClass1:
def test_case1(self):
print("這是pytest_usefixtures包中test_usefixtures子產品裡Testclass1中testcase1")
運作後結果:
test_case1測試函數調用
login
和
printids
兩個fixture。
2.1.3 autouse——自動應用
fixture()
的
autouse
參數,是fixture自動應用的辨別。
如果
autouse=True
,則在同作用域下的測試函數,會自動調用該fixture;如果
autouse=False
,則測試函數需要主動去調用該fixture。
autouse
預設是
False
。
示例4:
1.在
TestClass1
這個類中定義了一個
login
函數,聲明為fixture,并且
autouse
設定為
True
。
TestClass1
中的
test_case1
主動調用了
login
,但是其他測試函數未主動調用。
我們還寫了一個
TestClass2
類,類中有一個
test_case4
的測試函數。
import pytest
class TestClass1:
@pytest.fixture(autouse=True) # 啟用自動應用
def login(self):
print("登陸操作")
def test_case1(self,login): # 調用了login這個fixture函數
print("這是Testclass1中testcase1")
def test_case2(self): # 沒有調用
print('這是Testclass1中testcase2')
def test_case3(self): # 沒有調用
print('這是Testclass1中testcase3')
class TestClass2:
def test_case4(self):
print('這是Testclass2中的testcase4')
運作結果:
通過運作結果我們可以知道,當fixture的
autouse=True
的時候,在同作用域内的測試函數會自動調用fixture,非同作用域内的測試函數無法調用。
2.我們給
fixture()
帶上更多的參數,修改上面的demo,并設定fixture的
scope=‘class’
。
import pytest
@pytest.fixture(scope='class', autouse=True) # fixture的作用域設為class級别,啟用自動應用
def login():
print("登陸操作")
class TestClass1:
def test_case1(self):
print("這是Testclass1中testcase1")
def test_case2(self):
print('這是Testclass1中testcase2')
def test_case3(self):
print('這是Testclass1中testcase3')
class TestClass2:
def test_case4(self):
print('這是Testclass2中的testcase4')
運作結果:
通過運作結果我們可以看出,當設定
login
的
scope=‘class’
的使用,每一個測試類都會自動調用一次
login
。
autouse
的使用也是遵照fixture函數的設定來進行的。
2.2 fixture使用的靈活性
通過官方文檔的說明,我們知道了pytest的fixture系統是極其的靈活和強大的,官方文檔也為我們以下幾個靈活使用fixture的例子。
2.2.1 一個fixture函數可以調用其他的fixture
文章前面我們有示範過一個fixture函數調用其他fixture的例子。
代碼如下:
import pytest
@pytest.fixture()
def login():
print("登陸操作")
@pytest.fixture()
def logout(login):
yield
print("登出操作")
class TestClass1:
def test_case1(self,logout):
print("這是Testclass1中testcase1")
login()
實作了測試執行的前置操作,
logout()
實作了測試執行的後置操作。
logout()
調用了
login
,測試函數則直接調用了
logout
。
fixture在執行的時候是依次執行的。假如fixture A 調用了fixture B,那麼執行的時候會先執行fixture B,然後再執行fixture A。因為fixture B是fixture A的依賴條件。
是以我們運作上面代碼的時候,是會先調用
login()
然後再調用
logout(
)的。而
logout()
中的
yield
會讓測試用例執行的時候先執行
login
中的測試前置操作,然後再執行測試用例中的内容,最後執行
logout
中
yield
後面測試後置操作。
這樣的一個操作,可以将複雜的測試需求,變成一個一個簡單而又有組織性的的功能函數,更加友善後期進行維護。
2.2.2 fixture函數可被反複重用
兩個不同的測試函數可以調用同一個fixture,并且獲得各自的運作結果。測試函數之間并不會因為調用了同一個fixture而互相之間産生任何影響。
示例5:
示範代碼:
import pytest
@pytest.fixture()
def first_number(): #fixture函數,傳回1
return 1
@pytest.fixture()
def order(first_number): #fixture函數,調用了first_number這個fixture,并且傳回一個list
return [first_number]
def test_secondnum1(order): #調用order,并且在order傳回的list中添加 2 這個元素
order.append(2)
print(order)
assert order==[1,2] #斷言
def test_secondnum2(order): #也調用了order,并在order傳回的list中添加 3 這個元素
order.append(3)
print(order)
assert order==[1,3] #斷言
運作結果:
上面這段代碼我們聲明了:
兩個fixture——
first_number
和
order
。
first_number
有一個傳回值:
1
;
order
調用了
first_number
,并傳回一個清單:
[1]
。
我們還定義了:
兩個測試函數——
test_secondnum1
和
test_secondnum2
,這兩個測試函數都調用了
order
。
test_secondnum1
對
order
的傳回值做了一個
append(2)
的操作;
test_secondnum1
對
order
的傳回值做了一個
append(3)
的操作。
根據運作結果我們可以看出,兩個測試函數對調用的fixture都做出了各自的操作,并且得到各自的一個運作結果。兩者的運作結果沒有因為都調用了
order
而互相産生影響。
fixture可被反複重用這一特點,對于確定測試之間彼此不受影響是非常有用的。我們可以聲明一個通用的fixture函數,并使用這個特性來確定每個測試都能得到未受污染的測試資料,并且能從一個幹淨的初始狀态開始執行。
2.2.3 測試函數/fixture函數可以一次調用多個fixture
文章前面我們有介紹過如何使用usefixtures來調用多個fixture,由此我們可以知道,pytest是允許測試函數和fixture函數按照自己的需求一次調用多個fixture的。
示例6:
示範代碼:
import pytest
@pytest.fixture()
def first_number(): #第一個fixture,傳回1
return 1
@pytest.fixture()
def second_number(): #第二個fixture,傳回2
return 2
@pytest.fixture()
def third_number(): #第三個fixture,傳回1
return 3
@pytest.fixture()
def order(first_number,second_number): #調用first_number 和 second_number 傳回 [1,2]
return [first_number,second_number]
def test_case(order,third_number): #調用order 和 third_number
order.append(third_number) #對 order做append的操作,将third_number的傳回值加入list中
print(order)
assert order==[1,2,3] #斷言
運作結果:
示範代碼中我們聲明了:
四個fixture——
first_number
、
second_number
、
third_number
和
order
;
一個測試函數——
test_case
。
order
調用了
first_number
和
second_number
;
test_case
調用了
order
和
third_number
。他們都根據自己的需求調用了多個fixture,并且正常被執行。
2.2.4 同一測試執行期間,fixture可被多次請求
其實在看官方文檔的時候,我有點不太了解這部分的内容。當時看文字的描述感覺與“fixture能夠被重複調用“這一點有沖突,但是通過官方文檔的代碼示例,我才明白這兩點其實是不沖突的。
下面我們先看一下官方示例。
示例7:
示範代碼:
import pytest
@pytest.fixture
def first_entry():
return "a"
@pytest.fixture
def order():
return []
@pytest.fixture
def append_first(order, first_entry):
return order.append(first_entry)
def test_string_only(append_first,order, first_entry):
print(order)
assert order == [first_entry]
運作結果:
示例代碼中聲明了:
三個fixture函數——
first_entry
、
order
和
append_first
;
一個測試函數——
test_string_only
。
first_entry
傳回了一個str:
"a"
;
order
傳回一個空list:
[]
;
append_first
調用了
first_entry
和
order
,并且傳回
order.append(first_entry)
。
test_string_only
調用了f
irst_entry
、
order
和
append_first
,并做了一個列印
order
值的操作和斷言操作。
通過運作結果我們可以看到,
test_string_only
斷言成功,并且列印的
order
的值是
['a']
。
如果按照”fixture能夠被重複調用“這一特點看,列印的
order
不應該是空list的嗎?為什麼會是[‘a’]呢?
test_string_only
在執行期間,先執行
append_first
,而
append_first
調用了
order
,并且對
order
進行了添加元素的操作,更改了
order
的值,此時
order
傳回的值會存在緩存中。當
test_string_only
後面再去”調用“
order
的時候,其實和
append_first
引用的是同一個order對象,是以測試斷言才會成功。
通過pytest官方文檔我們知道,pytest一次隻緩存一個fixture的執行個體,這意味着在使用參數化fixture時,pytest可以在給定範圍内多次調用一個fixture。
當測試過程中先調用的fixture對其他fixture有依賴關系的話,在調用這個fixture的時候它所依賴的fixture也會被調用。是以後面測試執行過程中,如果再次有請求到這些fixture的話,fixture就不會被再次執行,此時會直接從緩存中擷取到之前fixture執行後傳回的資料來使用。
“fixture能夠被重複調用“——針對不同的測試過程,目的是為了保障每個測試執行時都是一個幹淨的環境。
“同一測試執行期間,fixture可被多次請求”——同一測試過程中,目的是為了保障在測試過程中,前後使用的資料不會被重置。
文末說明:
以上内容是我在閱讀pytest官方文檔後,依照個人了解進行整理。内容可能會有了解錯誤之處,歡迎大家留言指正。謝謝!
(*╹▽╹*)