天天看點

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

目錄

  • 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:

  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。")
    
           
  1. @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的調用。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

2.1.2 使用

@pytest.mark.usefixtures("fixturename")

根據pytest官方文檔的介紹,如果想讓某個fixture适用于類、子產品和項目中所有測試函數的話,會使用

usefixtures

來調用fixture。當然,單個測試函數也可以使用此方法來調用fixture。

使用

usefixtures

調用fixture和直接使用

fixturename

來調用fixture是有差別的,可總結為:

  1. 使用

    usefixtures

    調用的fixture隻能用于配置測試前系統的初始狀态,無法為測試用例提供測試資料;但使用

    fixturename

    調用的fixture卻可以實作這兩個功能。

示例:

1.聲明一個名為

login

的fixture,并通過

fixture()

傳入測試資料集。test_case1使用

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 
           
運作後發現,結果和我們預期的不一樣,

print(login)

列印出的是函數對象資訊,不是傳回值。
【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用
2.修改剛剛的代碼,使用fixturename來調用

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

           
運作後我們可以發現,結果和我們預期的一緻,把login的傳回值成功列印出來。
【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用
  1. usefixtures

    調用fixture的方法隻适用于測試函數,對于fixture函數不生效;使用

    fixturename

    調用fixture的方法對測試函數和fixture函數都适用。

示例:

1.以下demo想要實作執行測試用例前列印“登陸操作”,用例執行結束後列印“登出操作”。聲明

login

logout

為fixture函數,并且讓

logout

使用

usefixtures

的方法調用

login

,再讓

test_case1

調用

logout

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") 
           
通過運作結果我們發現,結果隻在執行測試函用例後列印了“登出操作”,與我們預期的結果不一緻。
【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用
2.修改上面的demo代碼,讓

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") 
        
           
運作後我們發現,結果與我們預期的一緻。
【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用
由此可以看出來,

userfixtures

的調用方法對于fixture函數無效。

下面我們将通過示例來示範userfixtures的使用方法:

為了示範效果,我們建立一個包,并在裡面建立一個conftest.py。用于定義fixture函數。關于conftest.py的相關内容,後面會單獨寫一遍文章詳細介紹。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

在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。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

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。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

3.整個項目中使用

pytest.ini

中配置

usefixtures

示範項目結構如圖:

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

對應檔案中的代碼如下:

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。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

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。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. 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,非同作用域内的測試函數無法調用。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. 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函數的設定來進行的。

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. 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] #斷言

           

運作結果:

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

上面這段代碼我們聲明了:

兩個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]  #斷言
    
           

運作結果:

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

示範代碼中我們聲明了:

四個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]
           

運作結果:

【pytest】2. pytest中的fixture (2) : fixture的聲明和調用1. fixture的聲明2. fixture的調用

示例代碼中聲明了:

三個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官方文檔後,依照個人了解進行整理。内容可能會有了解錯誤之處,歡迎大家留言指正。謝謝!

(*╹▽╹*)