您現在的位置是:首頁 > 人文

Pytest fixture及conftest詳解

由 大剛測試開發實戰 發表于 人文2022-12-28
簡介fixture(scope=“function”)def func_auto():“”“用例級別fixture,作用域單個用例”“”print(“n——-function級別的用例前置操作——-”)yield

case後面可以跟字串嗎

前言

fixture是在測試函式執行前後,由pytest執行的外殼函式。fixture中的程式碼可以定製,滿足多變的測試需求,包括定義傳入測試中的資料集、配置測試前系統的初始狀態、為批次測試提供資料來源等等。fixture是pytest的精髓所在,類似unittest中setup/teardown,但是比它們要強大、靈活很多,它的優勢是可以跨檔案共享。

一、Pytest fixture

1。pytest fixture幾個關鍵特性

有獨立的命名,並透過宣告它們從測試函式、模組、類或整個專案中的使用來啟用

按模組化的方式實現,每個fixture都可以互相呼叫

fixture可以實現unittest不能實現的功能,比如unittest中的測試用例和測試用例之間是無法傳遞引數和資料的,但是fixture卻可以解決這個問題

fixture的範圍從簡單的單元擴充套件到複雜的功能測試,允許根據配置和元件選項對fixture和測試用例進行引數化

2。Pytest fixture定義

定義fixture跟定義普通函式差不多,唯一區別就是在函式上加個裝飾器@pytest。fixture(),fixture命名不要用test_開頭,跟用例區分開。用例才是test_開頭的命名;

fixture裝飾器裡的scope有四個級別的引數:function(不寫預設這個)、class、module、session;

fixture可以有返回值,如果沒有return,預設會是None;用例呼叫fixture的返回值,就是直接把fixture的函式名稱作為引數傳入;

fixture可以返回一個元組、列表或字典;

測試用例可傳單個、多個fixture引數;

fixture與fixture間可相互呼叫;

3。Pytest fixture用法

1)用法一:作為引數使用

fixture的名字直接作為測試用例的引數,用例呼叫fixture的返回值,直接將fixture的函式名稱當做變數名稱;如果用例需要用到多個fixture的返回資料,fixture也可以返回一個元祖,list或字典,然後從裡面取出對應資料。

① 將fixture函式作為引數傳遞給測試用例

@pytest。fixture()def login(): print(“this is login fixture”) user = “chen” pwd = 123456 return user, pwddef test_login(login): “”“將fixture修飾的login函式作為引數傳遞給本用例”“” print(login) assert login[0] == “chen” assert login[1] == 123456 assert “chen” in str(login)

② 同一個用例中傳入多個fixture函式

@pytest。fixture()def user(): user = “cris” return user@pytest。fixture()def pwd(): pwd = “123456” return pwddef test_trans_fixture(user, pwd): “”“同一條用例中傳入多個fixture函式”“” print(user, pwd) assert “cris” in str(user) assert pwd == “123456”

③ fixture函式之間的相互傳遞

@pytest。fixture()def user2(): user = “cris” return user@pytest。fixture()def login_info(user2): “”“fixture與fixture函式之間的相互傳遞”“” pwd = “e10adc3949ba59abbe56e057f20f883e” return user2, pwddef test_assert_login_info(login_info): print(login_info) print(type(login_info)) assert login_info[0] == “cris” assert login_info[1] == “e10adc3949ba59abbe56e057f20f883e”

2)用法二:提供靈活的類似setup和teardown功能

Pytest的fixture另一個強大的功能就是在函式執行前後增加操作,類似setup和teardown操作,但是比setup和teardown的操作更加靈活;具體使用方式是同樣定義一個函式,然後用裝飾器標記為fixture,然後在此函式中使用一個yield語句,yield語句之前的就會在測試用例之前使用,yield之後的語句就會在測試用例執行完成之後再執行。

@pytest。fixture()def run_function(): print(“run before function。。。”) yield print(“run after function。。。”)def test_run_1(run_function): print(“case 1”)def test_run_2(): print(“case 2”)def test_run_3(run_function): print(“case 3”)

執行結果如下:

Pytest fixture及conftest詳解

常見的應用場景:@pytest。fixture可以用在selenium中測試用例執行前後開啟、關閉瀏覽器的操作:

@pytest。fixture()def fixture_driver(): driver = webdriver。Chrome() yield driver driver。quit()def test_baidu(fixture_driver): driver = fixture_driver driver。get(“http://www。baidu。com”) driver。find_element_by_id(‘kw’)。send_keys(“python fixture”) driver。find_element_by_id(‘su’)。click()

3)用法三:利用pytest。mark。usefixtures疊加呼叫多個fixture

如果一個方法或者一個class用例想要同時呼叫多個fixture,可以使用@pytest。mark。usefixtures()進行疊加。注意疊加順序,

先執行的放底層,後執行的放上層

。需注意:

① 與直接傳入fixture不同的是,@pytest。mark。usefixtures無法獲取到被fixture裝飾的函式的返回值;

② @pytest。mark。usefixtures的使用場景是:被測試函式需要多個fixture做前後置工作時使用;

@pytest。fixturedef func_1(): print(“用例前置操作——-1”) yield print(“用例後置操作——-1”)@pytest。fixturedef func_2(): print(“用例前置操作——-2”) yield print(“用例後置操作——-2”)@pytest。fixturedef func_3(): print(“用例前置操作——-3”) yield print(“用例後置操作——-3”)@pytest。mark。usefixtures(“func_3”) # 最後執行func_3@pytest。mark。usefixtures(“func_2”) # 再執行func_1@pytest。mark。usefixtures(“func_1”) # 先執行func_1def test_func(): print(“這是測試用例”)

執行結果:

Pytest fixture及conftest詳解

4)用法四:fixture自動使用autouse=True

當用例很多的時候,每次都傳這個引數,會很麻煩。fixture裡面有個引數autouse,預設是False沒開啟的,可以設定為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了,autouse設定為True,自動呼叫fixture功能。所有用例都會生效,

包括類中的測試用例和類以外的測試用例

@pytest。fixture(autouse=True, scope=“function”)def func_auto(): “”“autouse為True時,會作用於每一條用例”“” print(“\n——-用例前置操作——-”) yield print(“——-用例後置操作——-”)# func_auto函式的autouse=True時,無論是否使用usefixtures引用func_auto,都會執行func_auto@pytest。mark。usefixtures(“func_auto”)def test_01(): print(“case 1”)def test_02(): print(“case 2”)class Test: def test_03(self): print(“case 3”)

執行結果:

Pytest fixture及conftest詳解

4。Pytest fixture四種作用域

fixture(scope=‘function’,params=None,autouse=False,ids=None,name=None)

fixture裡面有個

scope

引數可以控制fixture的作用範圍:

function:每一個函式或方法都會呼叫

class:每一個類呼叫一次,一個類中可以有多個方法

module:每一個。py檔案呼叫一次,該檔案內又有多個function和class

session:多個檔案呼叫一次,可以跨。py檔案呼叫(通常這個級別會結合conftest。py檔案使用)

1)function級別

function預設模式為@pytest。fixture() 函式級別,即scope=“function”,scope可以不寫。每一個函式或方法都會呼叫,每個測試用例執行前都會執行一次function級別的fixture。

# @pytest。fixture(scope=“function”)等價於@pytest。fixture()@pytest。fixture(scope=“function”)def func_auto(): “”“用例級別fixture,作用域單個用例”“” print(“\n——-function級別的用例前置操作——-”) yield print(“——-function級別的用例後置操作——-”)# test_01會引用func_auto函式,test_02沒有用修飾器修飾,故不會引用def test_func_auto_fixture_1(func_auto): print(“func 1 print”)def test_func_auto_fixture_2(): print(“func 2 print”)

2)class級別

fixture的scope值還可以是class,此時則fixture定義的動作就會在測試類class的所有用例之前和之後執行,需注意:測試類中只要有一個測試用例的引數中使用了class級別的fixture,則在整個測試類的所有測試用例都會呼叫fixture函式

① 用例類中的測試用例呼叫fixture

執行fixture定義的動作,以及此測試類的所有用例結束後同樣要執行fixture指定的動作

@pytest。fixture(scope=“class”)def class_auto(): “”“類級別fixture,作用域整個類”“” print(“\n——-class級別的用例前置操作——-”) yield print(“——-class級別的用例後置操作——-”)class TestClassAutoFixture: # class級別的fixture任意一個用例引用即可 def test_class_auto_fixture_1(self, class_auto): print(“class 1 print”) def test_class_auto_fixture_2(self): print(“class 1 print”)

測試類中的第1條測試用例引用了fixture修飾的函式,則整個測試類的所有測試用例都會執行fixture函式的前置操作,在所有用例執行完成後,都會執行fixture函式的後置操作。

② 用例類外的測試用例呼叫fixture

如果在類外的函式中去使用class級別的fixture,則此時在測試類外每個測試用例中,fixture跟function級別的fixture作用是一致的,即

在類外的函式中引用了class級別的fixture,則在此函式之前和之後同樣去執行fixture定義的對應的操作

def test_class_auto_fixture(class_auto): print(“class 1 print”)

如下圖所示,測試類外的函式引用了class級別的fixture,則它的作用會等同於function級別的fixture,執行結果如下:

Pytest fixture及conftest詳解

3)module級別

在Python中module即。py檔案,當fixture定義為module時,則此fixture將在當前檔案中起作用。這裡需要特別說明的是,當fixture的scope定義為module時,只要當前檔案中有一個測試用例使用了fixture,不管這個用例是在類外,還是在類中,都會在當前檔案(模組)的所有測試用例執行之前去執行fixture定義的行為以及當前檔案的所有用例結束之後同樣去執行fixture定義的對應操作。

@pytest。fixture(scope=“module”)def module_auto(): “”“作用於整個py檔案”“” print(“\n——-module級別的用例前置操作——-”) yield print(“——-module級別的用例後置操作——-”) # 測試類外和測試類內的函式方法都呼叫了module級別的fixture,但整個py檔案只會生效一次fixture。def test_module_scope_out_class(module_auto): print(“case scope 01”) class TestScope1: def test_scope_01(self): print(“case scope 01”) def test_scope_02(self, module_auto): print(“case scope 02”) def test_scope_03(self): print(“case scope 03”)

若類中的方法分別呼叫了class級別的fixture和module級別的fixture,則會兩個fixture都生效:

# 順序在前面fixture會先執行def test_scope_01(self, module_auto, class_auto): print(“case scope 01”)

若類中的方法同時呼叫了function級別、class級別、module級別的fixture,則3種fixture會同時生效:

# 順序在前面fixture會先執行def test_scope_02(self, module_auto, class_auto, func_auto): print(“case scope 02”)

4)session級別(使用conftest。py共享fixture)

當fixture的scope定義為session時,是指在當前目錄下的所有用例之前和之後執行fixture對應的操作

fixture為session級別是可以跨.py模組呼叫

的,也就是當我們有多個。py檔案的用例的時候,如果多個用例只需呼叫一次fixture,那就可以設定為scope=“session”,並且寫到conftest。py檔案裡

使用方式:

① 定義測試用例檔案

② 在指定目錄下建立

conftest.py(固定命名,不可修改)

檔案,然後在conftest。py檔案中定義fixture方法,將scope指定為session,此時在當前目錄下只要有一個用例使用了此fixture,則就會在當前目錄下所有用例之前和之後會執行fixture定義的對應的操作。

@pytest。fixture(scope=“session”, )def session_auto(): “”“session級別的fixture,針對該目錄下的所有用例都生效”“” print(“\n——-session級別的用例前置操作——-”) yield print(“——-session級別的用例後置操作——-”)

定義了session級別的fixture,存放於該用例檔案的同一個目錄下的conftest。py檔案中,該目錄下的任一用例檔案中的任一測試用例,引用了這個session級別的fixture,則這個session級別的fixture會針對這整個用例檔案會生效。若存放在根目錄下,則針對整個工程的所有用例都會生效。

class TestSessionAutoFixture: # session級別的fixture任意一個用例引用即可 def test_session_auto_fixture_1(self, session_auto): print(“session 1 print”) def test_session_auto_fixture_2(self): print(“session 1 print”)def test_session_auto_fixture(): print(“session 1 print”)

執行結果如下:

Pytest fixture及conftest詳解

5。Pytest fixture其他引數用法

1)ids引數-修改用例結果名稱

@pytest。mark。parametrize() 還提供了第三個 ids 引數來自定義顯示結果。

stars = [“劉德華”, “張學友”, “黎明”, “郭富城”]# 利用列表生成式生成一個用例名稱的列表ids = [f“test-case-{d}” for d in range(len(stars))]@pytest。mark。parametrize(“name”, stars, ids=ids)def test_multi_param(name): print(f“my name is {name}”)

注:ids生成的用例名稱數量一定要和用例數量一致,否則會報錯,執行結果如下:

Pytest fixture及conftest詳解

2)name引數-重新命名fixture函式名稱

@pytest。fixture(name=“rename_get_user_info”)def get_user_info(): user_name = “周潤發” print(user_name)# 此處需傳入重新命名後的fixture函式名@pytest。mark。usefixtures(“rename_get_user_info”)def test_parametrize_by_use_fixtures(): “”“透過usefixtures裝飾器傳入fixture”“” print(f“test parametrize use fixtures”) def test_parametrize_by_fixture_name(rename_get_user_info): “”“將fixture函式名作為形參傳入”“” print(f“test parametrize use fixtures”)

3)params引數-提供返回值供測試函式呼叫

示例一

@pytest。fixture(params=[{“name”: “周潤發”}, {“age”: 61}, {“height”: 183}])def fix_func(request): # request為內建fixture # 使用request。param作為返回值供測試函式呼叫,params的引數列表中包含了做少元素,該fixture就會被呼叫幾次,分別作用在每個測試函式上 return request。param # request。param為固定寫法def test_fix_func(fix_func): print(f“fixture函式fix_func的返回值為:{fix_func}”) “”“列印結果如下: fixture函式fix_func的返回值為:{‘name’: ‘周潤發’} fixture函式fix_func的返回值為:{‘age’: 61} fixture函式fix_func的返回值為:{‘height’: 183} ”“”

示例二

params = [ {“case_id”: 1, “case_title”: “驗證正常新增車輛”, “car_name”: “蘇C99688”, “car_type”: 1, “origin”: 1, “expected”: “200”}, {“case_id”: 2, “case_title”: “驗證新增重複車輛”, “car_name”: “蘇C99688”, “car_type”: 1, “origin”: 1, “expected”: “500”}, {“case_id”: 3, “case_title”: “驗證車牌號為空”, “car_name”: “”, “car_type”: 2, “origin”: 1, “expected”: “500”}]@pytest。fixture(params=params)def add_car_params(request): return request。paramdef test_add_car(add_car_params): print(f“{add_car_params[‘case_id’]}-{add_car_params[‘case_title’]}-{add_car_params[‘car_name’]}”) “”“ 執行結果如下: 1-驗證正常新增車輛-蘇C99688 2-驗證新增重複車輛-蘇C99688 3-驗證車牌號為空- ”“”

Pytest fixture及conftest詳解

6。內建fixture

1)tmpdir和tmpdir_factory

內建的tmpdir和tmpdir_factory負責在測試開始執行前建立臨時檔案目錄,並在測試結束後刪除。如果測試程式碼要對檔案進行讀/寫操作,那麼可以使用tmpdir或tmpdir_factory來建立檔案或目錄。單個測試使用tmpdir,多個測試使用tmpdir_factory。tmpdir的作用範圍是函式級別,tmpdir_factory的作用範圍是會話級別。

def test_tmpdir(tmpdir): # tmpdir already has a path name associated with it # join() extends the path to include a filename # the file is created when it‘s written to a_file = tmpdir。join(’something。txt‘) # you can create directories a_sub_dir = tmpdir。mkdir(’anything‘) # you can create files in directories (created when written) another_file = a_sub_dir。join(’something_else。txt‘) # this write creates ’something。txt‘ a_file。write(’contents may settle during shipping‘) # this write creates ’anything/something_else。txt‘ another_file。write(’something different‘) # you can read the files as well assert a_file。read() == ’contents may settle during shipping‘ assert another_file。read() == ’something different‘def test_tmpdir_factory(tmpdir_factory): # you should start with making a directory # a_dir acts like the object returned from the tmpdir fixture a_dir = tmpdir_factory。mktemp(’mydir‘) # base_temp will be the parent dir of ’mydir‘ # you don’t have to use getbasetemp() # using it here just to show that it‘s available base_temp = tmpdir_factory。getbasetemp() print(’base:‘, base_temp) # the rest of this test looks the same as the ’test_tmpdir()‘ # example except I’m using a_dir instead of tmpdir a_file = a_dir。join(‘something。txt’) a_sub_dir = a_dir。mkdir(‘anything’) another_file = a_sub_dir。join(‘something_else。txt’) a_file。write(‘contents may settle during shipping’) another_file。write(‘something different’) assert a_file。read() == ‘contents may settle during shipping’ assert another_file。read() == ‘something different’

2)pytestconfig

內建的pytestconfig可以透過命令列引數、選項、配置檔案、外掛、執行目錄等方式來控制pytest。pytestconfig是request。config的快捷方式,它在pytest文件裡有時候被稱為“pytest配置物件”。

要理解pytestconfig如何工作,可以新增一個自定義的命令列選項,然後在測試中讀取該選項。

def pytest_addoption(parser): “”“”新增一個命令列選項“”“ parser。addoption( ”——env“, default=”test“, choices=[”dev“, ”test“, ”pre“], help=”enviroment parameter“)

以pytest_addoption新增的命令列選項必須透過外掛來實現,或者在專案頂層目錄的conftest。py檔案中完成。它所在的conftest。py不能處於測試子目錄下。

上述是一個傳入測試環境的命令列選項,接下來可以在測試用例中使用這些選項。

def test_option(pytestconfig): print(‘the current environment is:’, pytestconfig。getoption(‘env’))

# 執行測試pytest -s -q test_config。py::test_option

由於前面的pytest_addoption中定義的env的預設引數是test,所以透過pytestconfig。getoption獲取到的env的值就是test:

Pytest fixture及conftest詳解

3)其他內建fixture

cache:作用是儲存一段測試會話的資訊,在下一段測試會話中使用;

capsys:

capsys 有兩個功能:允許使用程式碼讀取 stdout 和 stderr;可以臨時禁制抓取日誌輸出

monkeypatch:

可以在執行期間對類或模組進行動態修改。在測試中,monkey patch 常用於替換被測試程式碼的部分執行環境,或者將輸入依賴或輸出依賴替換成更容易測試的物件或函式;

doctest_namespace:

doctest 模組是 Python 標準庫的一部分,藉助它,可以在函式的文件字串中放入示例程式碼,並透過測試確保有效。你可以使用 ——doctest-modules 標識搜尋並執行 doctest 測試用例;

recwarn:可以用來檢查待測程式碼產生的警告資訊;

recwarn 的值就像是一個警告資訊列表,列表裡的每個警告資訊都有4個屬性 category、message、filename、lineno。警告資訊在測試開始後收集,如果你在意的警告資訊出現在測試尾部,則可以在資訊收集前使用 recwarn。clear() 清除不需要的內容。除了 recwarn,pytest 還可以使用 pytest。warns() 來檢查警告資訊。

二、Pytest conftest全域性作用檔案詳解

Pytest支援在測試的目錄中,建立conftest。py檔案,進行全域性配置。

conftest.py檔案須知:

可以跨。py檔案呼叫,有多個。py檔案呼叫時,可讓conftest。py只調用了一次fixture,或呼叫多次fixture;

conftest。py與執行的用例要在同一個pakage下,並且有__init__。py檔案;

不需要import匯入conftest。py,pytest用例會自動識別該檔案,放到專案的根目錄下就可以全域性目錄呼叫了,如果放到某個package下,那就在package內有效,可有多個conftest。py;

conftest。py配置指令碼名稱是固定的,不能改名稱;

conftest。py檔案不能被其他檔案匯入;

所有同目錄測試檔案執行前都會執行conftest。py檔案;

推薦文章