1. 程式人生 > 實用技巧 >Pytest系列(五)fixture

Pytest系列(五)fixture

一、fixture 簡介

在 pytest 中加入fixture的目的是提供一個固定的基準,使測試能夠可靠、重複地執行,pytest 的 fixture 比傳統 xUnit 風格的setup/teardown 函式相比,有了巨大的改進:

  • fixture 具有明確的名稱,並通過在測試函式、模組、類或整個專案中宣告它們的使用來啟用。

  • fixture 是以模組化的方式實現的,因為每個fixture名稱都會觸發fixture函式,其本身也可以使用其他fixture。

  • fixture 管理從簡單的單元擴充套件到複雜的函式測試,允許根據配置和元件選項引數化fixture和測試,或者在函式、類、模組或整個測試會話範圍內重複使用fixture。

二、fixture 引數列表

@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None) 
def test():   
      print("fixture引數列表")
  • scope:可以理解成fixture的作用域,預設:function,還有class、module、package、session

  • autouse:預設:False,需要用例手動呼叫該fixture;如果是True,所有作用域內的測試用例都會自動呼叫該fixture

  • name:預設:裝飾器的名稱,同一模組的fixture相互呼叫建議寫不同的name

三、fixture 作用範圍

可以通過 scope 引數控制 fixture 的作用範圍:session>module>class>function

  • function:預設為function,每個函式或方法都呼叫一次

  • class:每個類呼叫一次,一個類中可以有多個方法

  • module:每個.py檔案呼叫一次

  • package:還處於測試階段...

  • session:每次執行呼叫一次

# test_fixture.py 檔案 
import pytest 
import requests 

@pytest.fixture 
def httpbin_url():        
      print("fixture 函式呼叫了一次")            
      return "http://httpbin.org"   

class TestHttp:        
      def test_get(self, httpbin_url):                    
            res = requests.get(httpbin_url + "/get")                        
            assert res.status_code == 200            
      def test_post(self, httpbin_url):                    
            res = requests.post(httpbin_url + "/post")                        
            assert res.status_code == 200            
      def test_del(self, httpbin_url):                    
            res = requests.delete(httpbin_url + "/delete")                        
            assert res.status_code == 200         

if __name__ == '__main__':        
      pytest.main(['-s', 'test_fixture.py'])

四、呼叫 fixture 的三種方法

4.1 將 fixture 作為函式引數

Fixtures 允許測試用例引入預先定義好的 fixture 初始化準備函式(函式名不能以test開頭),函式可以通過 @pytest.fixture 裝飾器註冊成為一個 fixture 函式,測試用例可以使用 fixture 函式名來接收 fixture 物件。

import pytest 
import requests 
@pytest.fixture 
def httpbin():      
      return ("get", "http://httpbin.org") 

# 也可以以元祖的形式返回多個引數   
def test_get(httpbin):      
      res = requests.request(httpbin[0], httpbin[1])        
      assert res.status_code == 200

4.2 使用裝飾器 @pytest.mark.usefixtures() 修飾需要執行的用例

如果一個方法或者一個class用例想要同時呼叫多個 fixture,可以使用 @pytest.mark.usefixture() 進行疊加。注意疊加順序,先執行的放底層,後執行的放上層。

注:usefixtures 與直接傳入 fixture 引數的區別在於,usefixture 無法獲取到返回值

@pytest.fixture 
def httpbin():      
      print("開始測試-----")     

@pytest.mark.usefixtures("httpbin") 
def test_get():      
      res = requests.get("http://httpbin.org")        
      assert res.status_code == 200

4.3 fixture自動使用autouse=True

當用例很多的時候,每次都傳這個引數就會很麻煩。可以將 fxiture 引數 autouse 設定為True,開啟自動使用fixture功能,這樣用例就不用每次都去傳參了。

import pytest 
import requests   
@pytest.fixture(autouse=True) 
def httpbin():   
      print("開始測試-----")  

def test_get():   
      res = requests.get("http://httpbin.org")     
      assert res.status_code == 200

4.4 conftest.py

若要使用來自多個測試檔案的 fixture 函式,可以將其移至conftest.py檔案,它會自動被pytest發現,而無需匯入 fixture 函式。

一個工程下可以建多個 conftest.py 的檔案,一般在工程根目錄下設定的 conftest 檔案起到全域性作用。在不同子目錄下也可以放 conftest.py 的檔案,作用範圍只能在該目錄下生效。

注:conftest.py 檔名是固定的,且不能跨模組呼叫

# conftest.py 檔案 
import pytest   

@pytest.fixture(autouse=True) 
def httpbin():   
      print("開始測試-----")     

# test_demo.py 檔案 
import requests 
def test_get():      
      res = requests.get("http://httpbin.org")       
      assert res.status_code == 200

五、引數化 fixture

可以新增 params 對 Fixture 函式進行引數化,通過使用 request 物件來訪問每個引數;也可在引數化Fixture方法時使用 pytest.param(),其方式與@ pytest.mark.parametrize一樣。

@pytest.fixture(scope="class", params=["get", "post", "delete"]) 
def method(request):     
      return request.param      
# fixture函式通過特殊的request物件訪問每個引數   
class TestHttp:     
      def test_get(self, method):             
            assert 1

六、fixture 實現 teardown

pytest.fixture 採用 yield 實現 setup 和 teardown 操作,yield 前面的程式碼為 setup 程式碼,yield 後面的程式碼為 teardown 程式碼.

@pytest.fixture(scope="function") 
def func(request):      
      driver = webdriver.Chrome()       
      yield driver        
      driver.quit() 

class TestFuncA:      
      def test_01(self, func):              
            func.get("https://www.baidu.com")                
            assert 1

注:如果程式碼在 yield關鍵字之前發生異常,則不會呼叫yield 之後的程式碼。可以選擇利用請求上下文物件的 addfinalizer 方法來註冊完成函式。

import pytest 
from selenium import webdriver 

@pytest.fixture(scope="function") 
def func(request):      
      driver = webdriver.Chrome()        
      def fin():              
            driver.quit()        
      request.addfinalizer(fin)        
      return driver   

class TestFuncA:      
      def test_01(self, func):              
            func.get("https://www.baidu.com")                
            assert 1

addfinalizer 與 yield 的區別:

  • 可以註冊多個完成函式

  • 無論 fixture 設定程式碼是否引發異常,完成函式將始終被呼叫