此前一直認為fixture就叫python中的裝飾器,學習后才發現decorators才是裝飾器,fixture是pytest框架的夾具,只是通過裝飾器去定義和使用。所以要了解fixture就得先了解python裝飾器。
一、裝飾器(decorators)
1.定義
裝飾器(decorators)是 Python 中的一種高級功能,允許動態地修改函數或類的行為。裝飾器是一種函數 ,它接受一個函數作為參數,并返回一個新的函數或修改原來的函數。
2.使用場景
日志記錄(函數的調用信息、參數、返回值)、性能分析(函數執行時間)、權限控制(函數訪問權限)、緩存(函數結果緩存)
3.裝飾器使用方法
我采用一個方法來寫清楚它的使用方法以及對參數的接收處理,按步驟查看注釋
1)基本語法
#1.聲明裝飾器函數new_function,用original_function接收原始函數old_function
def new_function(original_function):#3.聲明一個inner_function方法---接受原始函數的參數并進行處理。#*args表示接收任意數量的位置參數--非關鍵字參數,并打包成一個元組。#**kwarg表示接收任意數量的關鍵字參數--如key=value,并打包成一個字典。#兩種方式作用為了兼容所有傳進來的參數類型def inner_function(*args, **kwargs):# 4.調用原始函數前添加的新操作print("添加的前置操作,對關鍵字參數進行處理")#對關鍵字參數處理--元組不支持修改print("傳進來的元組參數:",args)print("傳進來的關鍵字參數:",kwargs)kwargs["a"]=10# 5.將修改后的參數傳回result = original_function(*args, **kwargs)# 6.調用原始函數后添加的新操作print("添加的后置操作")return result#7.返回新函數inner_functionreturn inner_function#8.使用裝飾器@new_function,現在調用的函數實際是處理后的新函數
#等同于old_function= new_function(old_function)
@new_function
#2.聲明原始函數old_function
def old_function(arg1,arg2,a=None):print("原始函數")print("修改過后的a:",a)#9.帶參數調用函數
old_function(1, 3, a=4)
執行結果:
多個裝飾器裝飾方法時,會依次調用。
2)內置裝飾器
除了裝飾方法以外,裝飾器還可以裝飾類。Python 提供了一些內置的裝飾器,例如:
@staticmethod: 將方法定義為靜態方法,不需要實例化類可直接調用。
@classmethod: 將方法定義為類方法,第一個參數是類本身(通常命名為 cls)。
@property: 將方法轉換為屬性,使其可以像屬性一樣訪問。
二、fixture夾具
如果理解了裝飾器是什么,就不難理解fixture夾具了。在pytest測試框架中,夾具(fixture–英文釋義固定器械)是一種用于為測試用例提供預置環境或共享資源的機制。個人理解fixture就是pytest封裝好了的一個裝飾器函數名稱----@pytest.fixture。既然fixture是封裝好的,那一定有指定的參數和調用方法。
1.fixture的參數
如上圖封裝好的fixture方法可見,fixture有五種傳參:
#實例用法
@pytest.fixture(scope='',autouse='',params="",ids="",name="")
1)scope—fixture的作用范圍,一共有四種
function(函數級):每一個函數或方法都會調用
class(類級別):每個測試類只運行一次
module(模塊級):每一個.py文件調用一次
package(包級):每一個python包只調用一次(暫不支持)
session(會話級):每次會話只需要運行一次,會話內所有方法及類,模塊都共享這個方法
執行優先級:session > module > class > function
這個地方先不舉例,和2聯合起來舉例,通過2的示例,將會很清楚scope的執行過程
2)autouse—是否自動執行
默認為False,設置為True后設置的執行范圍scope里面包含的所有用例都會執行這個方法。不用再手動在每個要裝飾的方法上使用@pytest.mark.usefixture(“fixturename”)。
示例代碼:
import logging
import pytest
from allure_commons import fixture# 會話級別的 fixture - 整個測試運行期間只執行一次
@pytest.fixture(scope="session", autouse=True)
def session_fixture():print("\n===== session 范圍 fixture 開始 (整個測試會話只執行一次) =====")yieldprint("\n===== session 范圍 fixture 結束 (所有測試完成后執行) =====")# 模塊級別的 fixture - 每個測試模塊只執行一次
@pytest.fixture(scope="module", autouse=True)
def module_fixture():print("\n***** module 范圍 fixture 開始 (每個.py文件只執行一次) *****")yieldprint("\n***** module 范圍 fixture 結束 (文件所有測試完成后執行) *****")# 類級別的 fixture - 每個測試類執行一次
@pytest.fixture(scope="class", autouse=True)
def class_fixture():print("\n------ class 范圍 fixture 開始 (每個測試類只執行一次) ------")yieldprint("\n------ class 范圍 fixture 結束 (類中所有測試完成后執行) ------")# 函數級別的 fixture - 每個測試函數執行一次
@pytest.fixture(scope="function", autouse=True)
def function_fixture():print("\n>>>>>> function 范圍 fixture 開始 (每個測試方法執行一次) >>>>>>")yieldprint("\n<<<<<< function 范圍 fixture 結束 (每個測試方法完成后執行) <<<<<<")def test_outside_class():print("執行類外的測試函數")assert Trueclass TestClass:def test_case1(self):print("執行test_case1")assert Truedef test_case2(self):print("執行test_case2")assert True
執行打印:
3)params—參數
示例代碼:
import pytest@pytest.fixture(scope="function",params=[1,2,3,4])
#固定寫法用request表示參數
def function_fixture(request):#固定寫法提取參數yield request.param#這里用注釋來使用裝飾器
@pytest.mark.usefixtures("function_fixture")
def test_param():print("test")
運行結果:
4)ids—給params參數每一個值設置變量名
上述params示例中,可以看到傳入了參數1、2、3、4。但是沒有參數名稱,不太方便去使用。這個ids就是給參數設置變量名。
示例代碼:
import pytest@pytest.fixture(scope="function",params=[1,2,3,4],ids=['p1','p2','p3','p4'])
#固定寫法用request表示參數
def function_fixture(request):#固定寫法提取參數yield request.param#直接在測試函數參數中聲明fixture名稱來獲取參數
def test_param(function_fixture):print(f"當前參數值: {function_fixture}")print("test")
運行結果:
5)name–給fixture標記的方法取別名
例如以上的function_fixture我給它起名為fixname
示例代碼:
import pytest@pytest.fixture(scope="function",params=[1,2,3,4],ids=['p1','p2','p3','p4'],name="fixname")
#固定寫法用request表示參數
def function_fixture(request):#固定寫法提取參數yield request.param#直接在測試函數參數中聲明fixture名稱來獲取參數
def test_param(fixname):print(f"當前參數值: {fixname}")print("test")
運行結果:
2.fixture配置conftest.py使用
上述代碼中是在每個測試用例py中定義的fixture,測試過程中會有很多通用的操作,豈不是每個用例中都要定義一遍,這里就用到了conftest.py來管理。
conftest.py主要作用是用于定義和共享測試配置、Fixture以及其他測試相關的設置、避免重復代碼,提高測試代碼的可維護性和可重用性,使測試代碼本身更專注于測試邏輯—簡單的說就是可以跨測試用例共享數據和資源,對測試前后的配置和數據處理等操作
注:和conftest.py同一目錄以及子目錄的測試均可使用其中裝飾器。
示例代碼:
test_conf.py
import pytestdef test_conf():print("測試conftest.py文件,即使這里沒有使用fixture也會調用!!")
conftest.py
import pytest@pytest.fixture(scope="function", autouse=True)
def function_fixture():print("\n>>>>>> function 范圍 fixture 開始 (每個測試方法執行一次) >>>>>>")yieldprint("\n<<<<<< function 范圍 fixture 結束 (每個測試方法完成后執行) <<<<<<")
目錄結構:
運行結果:
通常配合conftest.py用的還有鉤子hook函數。
三、總結
1、decorators裝飾器是一種函數 ,它接受一個函數作為參數,并返回一個新的函數或修改原來的函數。----個人理解裝飾器就是封裝好了一個函數,然后采用"@函數名稱()"來注釋另一個函數以使用該裝飾器,達到某些目的。
2、fixture測試夾具,本質上是一個函數,為測試提供可重用的環境支持。用@pytest.fixture()裝飾器將一個函數標記為pytest可識別的fixture夾具。
3、可以用conftest.py來管理共享的裝飾器。