開發與維運

Python 自動化測試(三): pytest 參數化測試用例構建

本文節選自霍格沃玆測試學院內部教材,文末鏈接進階學習。

在之前的文章中主要分享了 pytest 的實用特性,接下來講 Pytest 參數化用例的構建。

如果待測試的輸入與輸出是一組數據,可以把測試數據組織起來用不同的測試數據調用相同的測試方法。參數化顧名思義就是把不同的參數,寫到一個集合裡,然後程序會自動取值運行用例,直到集合為空便結束。pytest 中可以使用 @pytest.mark.parametrize 來參數化。

使用 parametrize 實現參數化

parametrize( ) 方法源碼:

def parametrize(self,argnames, argvalues, indirect=False, ids=None, \
    scope=None):
  • 主要參數說明

    • argsnames :參數名,是個字符串,如中間用逗號分隔則表示為多個參數名
    • argsvalues :參數值,參數組成的列表,列表中有幾個元素,就會生成幾條用例
  • 使用方法

    • 使用 @pytest.mark.paramtrize() 裝飾測試方法
    • parametrize('data', param) 中的 “data” 是自定義的參數名,param 是引入的參數列表
    • 將自定義的參數名 data 作為參數傳給測試用例 test_func
    • 然後就可以在測試用例內部使用 data 的參數了

創建測試用例,傳入三組參數,每組兩個元素,判斷每組參數裡面表達式和值是否相等,代碼如下:

@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+5",7),("7*5",30)])
def test_eval(test_input,expected):
    # eval 將字符串str當成有效的表達式來求值,並返回結果
    assert eval(test_input) == expected

運行結果:

plugins: html-2.0.1, rerunfailures-8.0, xdist-1.31.0, ordering-0.6, \
forked-1.1.3, allure-pytest-2.8.11, metadata-1.8.0
collecting ... collected 3 items

test_mark_paramize.py::test_eval[3+5-8] 
test_mark_paramize.py::test_eval[2+5-7] 
test_mark_paramize.py::test_eval[7*5-35] 

============================== 3 passed in 0.02s ===============================

整個執行過程中,pytest 將參數列表 [("3+5",8),("2+5",7),("7*5",30)] 中的三組數據取出來,每組數據生成一條測試用例,並且將每組數據中的兩個元素分別賦值到方法中,作為測試方法的參數由測試用例使用。

多次使用 parametrize

同一個測試用例還可以同時添加多個 @pytest.mark.parametrize 裝飾器, 多個 parametrize 的所有元素互相組合(類似笛卡兒乘積),生成大量測試用例。

場景:比如登錄場景,用戶名輸入情況有 n 種,密碼的輸入情況有 m 種,希望驗證用戶名和密碼,就會涉及到 n*m 種組合的測試用例,如果把這些數據一一的列出來,工作量也是非常大的。pytest 提供了一種參數化的方式,將多組測試數據自動組合,生成大量的測試用例。示例代碼如下:

@pytest.mark.parametrize("x",[1,2])
@pytest.mark.parametrize("y",[8,10,11])
def test_foo(x,y):
    print(f"測試數據組合x: {x} , y:{y}")

運行結果:

plugins: html-2.0.1, rerunfailures-8.0, xdist-1.31.0, ordering-0.6,\
 forked-1.1.3, allure-pytest-2.8.11, metadata-1.8.0
collecting ... collected 6 items

test_mark_paramize.py::test_foo[8-1] 
test_mark_paramize.py::test_foo[8-2] 
test_mark_paramize.py::test_foo[10-1] 
test_mark_paramize.py::test_foo[10-2] 
test_mark_paramize.py::test_foo[11-1] 
test_mark_paramize.py::test_foo[11-2] 

分析如上運行結果,測試方法 test_foo( ) 添加了兩個 @pytest.mark.parametrize() 裝飾器,兩個裝飾器分別提供兩個參數值的列表,2 * 3 = 6 種結合,pytest 便會生成 6 條測試用例。在測試中通常使用這種方法是所有變量、所有取值的完全組合,可以實現全面的測試。

@pytest.fixture 與 @pytest.mark.parametrize 結合

下面講結合 @pytest.fixture 與 @pytest.mark.parametrize 實現參數化。

如果測試數據需要在 fixture 方法中使用,同時也需要在測試用例中使用,可以在使用 parametrize 的時候添加一個參數 indirect=True,pytest 可以實現將參數傳入到 fixture 方法中,也可以在當前的測試用例中使用。

parametrize 源碼:

def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None):

indirect 參數設置為 True,pytest 會把 argnames 當作函數去執行,將 argvalues 作為參數傳入到 argnames 這個函數裡。創建“test_param.py”文件,代碼如下:

# 方法名作為參數
test_user_data = ['Tome', 'Jerry']
@pytest.fixture(scope="module")
def login_r(request):
    # 通過request.param獲取參數
    user = request.param
    print(f"\n 登錄用戶:{user}")
    return user

@pytest.mark.parametrize("login_r", test_user_data,indirect=True)
def test_login(login_r):
    a = login_r
    print(f"測試用例中login的返回值; {a}")
    assert a != ""

運行結果:

plugins: html-2.0.1, rerunfailures-8.0, xdist-1.31.0, ordering-0.6,\
 forked-1.1.3, allure-pytest-2.8.11, metadata-1.8.0
collecting ... collected 2 items

test_mark_paramize.py::test_login[Tome] 
test_mark_paramize.py::test_login[Jerry] 

============================== 2 passed in 0.02s ===============================

Process finished with exit code 0

 登錄用戶:Tome PASSED           [ 50%]測試用例中login的返回值; Tome

 登錄用戶:Jerry PASSED           [100%]測試用例中login的返回值; Jerry

上面的結果可以看出,當 indirect=True 時,會將 login_r 作為參數,test_user_data 被當作參數傳入到 login_r 方法中,生成多條測試用例。通過 return 將結果返回,當調用 login_r 可以獲取到 login_r 這個方法的返回數據。

更多技術文章分享及測試資料點此獲取

Leave a Reply

Your email address will not be published. Required fields are marked *