【pytest】(十)fixture參數化-巧用params和ids優雅的創建測試數據


我們都知道參數化。

比如我要測試一個查詢接口/test/get_goods_list,這個接口可以查詢到商品的信息。
在請求中,我可以根據請參數goods_status的不同傳值,可以查詢到對應狀態的商品數據,比如:1-未銷售2-銷售中3-已售罄

那么在編寫自動化測試case的時候,在斷言里就要分別驗證到這3種狀態的商品數據。
通常,在執行case之前,會去數據庫分別插入對應狀態的商品數據,來滿足測試需求。
而在pytest框架中,我喜歡用fixture()去實現測試數據的准備和清理工作。
於是,2種實現方式瞬間出現:

  • 寫3個case,只有傳參不一樣。對應寫3個fixture來分別初始化3種狀態的商品數據。
  • 寫1個case,在case里用@pytest.mark.parametrize()進行參數化。只寫一個fixture一次性的插入3種狀態的商品數據。

在這2個方法里,顯然第二種更優雅,避免了case的冗余代碼。
但是一把梭的插入所有的測試數據還是差點意思,如果我只想執行其中的某一個數據的case,那么其他2個不必要的數據也生成了。

所以,我想要的樣子是,可以自由的根據執行的case的參數,去對應的初始化測試數據。
具體點的描述就是:參數化里3個參數,我只執行2-銷售中的時候,只去插入2-銷售中這一種狀態的數據。

網上搜的都是簡單的fixture參數化的東西,達不到我想要的需求。於是乎我自己去翻閱官方文檔,發現可以用fixture中的params和ids
這2個參數去實現我的需求。

一、fixture中的params

params是一個列表,用來存放我們要參數化的值。
舉例:這里的代碼放了參數1參數2 這2個參數,2個測試case,都會用params里的參數去分別執行2次。

import pytest


@pytest.fixture(params=['參數1', '參數2'])
def my_fixture(request):
    return request.param


def test_fixtures_01(my_fixture):
    print('\n 執行test_fixtures_01')
    print(my_fixture)


def test_fixtures_02(my_fixture):
    print('\n 執行test_fixtures_02')
    print(my_fixture)

運行一下:

collecting ... collected 4 items

test_ids.py::test_fixtures_01[\u53c2\u65701] PASSED                      [ 25%]
 執行test_fixtures_01
參數1

test_ids.py::test_fixtures_01[\u53c2\u65702] PASSED                      [ 50%]
 執行test_fixtures_01
參數2

test_ids.py::test_fixtures_02[\u53c2\u65701] PASSED                      [ 75%]
 執行test_fixtures_02
參數1

test_ids.py::test_fixtures_02[\u53c2\u65702] PASSED                      [100%]
 執行test_fixtures_02
參數2


============================== 4 passed in 0.03s ==============================

Process finished with exit code 0

二、fixture中的ids

ids也是要結合着params一起使用的。當有多個 params 時,針對每一個 param,可以指定一個id
然后,這個 id 會變成測試用例名字的一部分。如果沒有提供 id,則 id 將自動生成。

import pytest


@pytest.fixture(params=['參數1', '參數2'], ids=["id-01", "id-02"])
def my_fixture(request):
    return request.param


def test_fixtures_01(my_fixture):
    print('\n 執行test_fixtures_01')
    print(my_fixture)

運行下,結果里case名稱后分別帶了 id:[id-01][id-02]

collecting ... collected 2 items

test_ids.py::test_fixtures_01[id-01] PASSED                              [ 50%]
 執行test_fixtures_01
參數1

test_ids.py::test_fixtures_01[id-02] PASSED                              [100%]
 執行test_fixtures_01
參數2


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

Process finished with exit code 0

三、利用ids實現需求

ids的賦值除了上述的方式之外,還有一種,也就是幫我實現需求的一種,直接看代碼。

import pytest


def init_data(fixture_value):
    if fixture_value == 1:
        return "unsold"
    elif fixture_value == 2:
        return "onSale"
    elif fixture_value == 3:
        return "sellOut"


@pytest.fixture(params=[1, 2, 3], ids=init_data)
def my_method(request):
    req_param = request.param
    print("\n參數為:【{}】,執行sql--插入【{}】狀態的數據".format(req_param, req_param))
    yield req_param
    print("\n執行sql--清理參數為【{}】的測試數據".format(req_param, req_param))
    print("\n----------------------------------------")


def test_01(my_method):
    print("\n正在執行【{}】的case--".format(my_method))


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

示例代碼就沒有去真正的寫一個接口的case了,因為太(lan)忙了,所以直接用print()打印出我要做的事兒。

上述代碼中,重點就是3個部分:

  • test_01(),這是測試case
  • my_method()這是我定義的fixture函數
  • init_data()這個是用來初始化測試數據的函數

1、test_01()

測試case沒什么說的,一般來說接口的case里的組成就是:傳參調用測試接口斷言
case里傳入了my_method函數,這是調用fixture的一種方式。

def test_01(my_method):
    print("\n正在執行[{}]的case--".format(my_method))

2、my_method()

我定義了my_method這個fixture去進行case執行之前的測試數據處理。

@pytest.fixture(params=[1, 2, 3], ids=init_data)
def my_method(request):
    req_param = request.param
    print("\n參數為:{}".format(req_param))
    yield req_param
    print("\n執行sql--清理參數為【{}】的測試數據".format(req_param))
    print("\n----------------------------------------")

params=[1,2,3]就是相當於我接口請求體里查詢不同狀態商品的數據對應的參數,1-未銷售2-銷售中3-已售罄

ids在上面單獨介紹的例子中是直接賦值的,但是在這里,我是把一個函數賦給了它,這個函數就是init_data()
當然了,為了滿足后面我的指定參數執行case的需求,init_data要返回具體的id才行。

3、init_data()

init_data(fixture_value)函數里傳入的fixture_value,其實就是fixture函數里的params=[1, 2, 3]
return出來的則是這個參數對應的id,分別是"未銷售""銷售中""已售罄",那么我就可以通過pytest命令 加上 -k來指定要運行的case。

def init_data(fixture_value):
    if fixture_value == 1:
        return "unsold"
    elif fixture_value == 2:
        return "onSale"
    elif fixture_value == 3:
        return "sellOut"

先不用-k,看下整個的運行結果。

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

運行結果:

collected 3 items                                                                                                                                                            

test_my_fixture.py::test_01[\u672a\u9500\u552e]
參數為:【1】,執行sql--插入【1】狀態的數據

正在執行【1】的case--
PASSED
執行sql--清理參數為【1】的測試數據

----------------------------------------

test_my_fixture.py::test_01[\u9500\u552e\u4e2d]
參數為:【2】,執行sql--插入【2】狀態的數據

正在執行【2】的case--
PASSED
執行sql--清理參數為【2】的測試數據

----------------------------------------

test_my_fixture.py::test_01[\u5df2\u552e\u7f44]
參數為:【3】,執行sql--插入【3】狀態的數據

正在執行【3】的case--
PASSED
執行sql--清理參數為【3】的測試數據

----------------------------------------


============================================================================= 3 passed in 0.03s =============================================================================

可以看到,在case執行之前,就插入了3種狀態的測試數據,並且運行了3個case。
接下來,我們用-k來執行 id是"onSale"的case:

if __name__ == '__main__':
    pytest.main(['-s', '-v', '-k', "onSale", 'test_my_fixture.py::test_01'])

運行結果:

collected 3 items / 2 deselected / 1 selected                                                                                                                                

test_my_fixture.py::test_01[onSale]
參數為:【2】,執行sql--插入【2】狀態的數據

正在執行【2】的case--
PASSED
執行sql--清理參數為【2】的測試數據

----------------------------------------


====================================================================== 1 passed, 2 deselected in 0.03s ======================================================================

可以看到,找到了3個case,但是只執行了我們制定要運行的case。

在我以前寫的case中,其實並沒有這樣寫。之前我們寫了一個mock服務,於是乎我就把一些會變化的請求參數也配置進去了,然后根據我
傳參的不同,去拿到我想要的請求body,最后再去請求我要測試的接口。

現在新換了個地方,每天忙於dian業dian務dian,工作中突然有了這個想法,於是乎抽空找尋了下方法。這個方法我在后面的自動化服務搭建
中會去運用,屆時有新想法再分享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM