咱們都知道參數化。sql
好比我要測試一個查詢接口/test/get_goods_list
,這個接口能夠查詢到商品的信息。
在請求中,我能夠根據請參數goods_status的不一樣傳值,能夠查詢到對應狀態的商品數據,好比:1-未銷售
、2-銷售中
、3-已售罄
。數據庫
那麼在編寫自動化測試case的時候,在斷言裏就要分別驗證到這3種狀態的商品數據。
一般,在執行case以前,會去數據庫分別插入對應狀態的商品數據,來知足測試需求。
而在pytest框架中,我喜歡用fixture()
去實現測試數據的準備和清理工做。
因而,2種實現方式瞬間出現:框架
@pytest.mark.parametrize()
進行參數化。只寫一個fixture一次性的插入3種狀態的商品數據。在這2個方法裏,顯然第二種更優雅,避免了case的冗餘代碼。
可是一把梭的插入全部的測試數據仍是差點意思,若是我只想執行其中的某一個數據的case,那麼其餘2個沒必要要的數據也生成了。函數
因此,我想要的樣子是,能夠自由的根據執行的case的參數,去對應的初始化測試數據。
具體點的描述就是:參數化裏3個參數,我只執行2-銷售中
的時候,只去插入2-銷售中
這一種狀態的數據。測試
網上搜的都是簡單的fixture參數化的東西,達不到我想要的需求。因而乎我本身去翻閱官方文檔,發現能夠用fixture中的params和ids
這2個參數去實現個人需求。code
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
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
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()
,這是測試casemy_method()
這是我定義的fixture函數init_data()
這個是用來初始化測試數據的函數測試case沒什麼說的,通常來講接口的case裏的組成就是:傳參
、調用測試接口
、斷言
。
case裏傳入了my_method
函數,這是調用fixture
的一種方式。
def test_01(my_method): print("\n正在執行[{}]的case--".format(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(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,工做中忽然有了這個想法,因而乎抽空找尋了下方法。這個方法我在後面的自動化服務搭建 中會去運用,屆時有新想法再分享。