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

咱們都知道參數化。sql

好比我要測試一個查詢接口/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個參數去實現個人需求。code

1、fixture中的params

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

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

2、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]get

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

3、利用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()這個是用來初始化測試數據的函數

一、test_01()

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

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

二、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才行。

三、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,工做中忽然有了這個想法,因而乎抽空找尋了下方法。這個方法我在後面的自動化服務搭建 中會去運用,屆時有新想法再分享。

相關文章
相關標籤/搜索