函數數據參數化
方便測試函數對測試數據的獲取。 方法: parametrize(argnames, argvalues, indirect=False, ids=None, scope=None) 常用參數: argnames:參數名 argvalues:參數對應值,類型必須為list 當參數為一個時格式:[value] 當參數個數大於一個時,格式為:[(param_value1,param_value2.....),(param_value1,param_value2.....)] 使用方法: @pytest.mark.parametrize(argnames,argvalues) ️ 參數值為N個,測試方法就會運行N次
單個參數示例:
import pytest class Test_ABC: def setup_class(self): print("------->setup_class") def teardown_class(self): print("------->teardown_class") @pytest.mark.parametrize("a", [3, 6]) # a參數被賦予兩個值,函數會運行兩遍 def test_a(self, a): # 參數必須和parametrize里面的參數一致 print("test data:a=%d" % a) assert a % 3 == 0
多個參數示例:
import pytest class Test_ABC: def setup_class(self): print("------->setup_class") def teardown_class(self): print("------->teardown_class") @pytest.mark.parametrize("a,b", [(1, 2), (0, 3)]) # 參數a,b均被賦予兩個值,函數會運行兩遍 def test_a(self, a, b): # 參數必須和parametrize里面的參數一致 print("test data:a=%d,b=%d" % (a, b)) assert a + b == 3
函數返回值類型示例:
import pytest def return_test_data(): return [(1,2),(0,3)] class Test_ABC: def setup_class(self): print("------->setup_class") def teardown_class(self): print("------->teardown_class") @pytest.mark.parametrize("a,b", return_test_data()) # 使用函數返回值的形式傳入參數值 def test_a(self, a, b): print("test data:a=%d,b=%d" % (a, b)) assert a + b == 3
附上parametrize 源碼解析:
def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None): """ Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources see about setting indirect to do it rather at test setup time. # 使用給定argnames的argValue列表向基礎測試函數添加新的調用。在收集階段執行參數化。 :arg argnames: a comma-separated string denoting one or more argument names, or a list/tuple of argument strings. # 參數名:使用用逗號分隔的字符串,或列表或元祖,表示一個或多個參數名 :arg argvalues: The list of argvalues determines how often a test is invoked with different argument values. If only one argname was specified argvalues is a list of values. If N argnames were specified, argvalues must be a list of N-tuples, where each tuple-element specifies a value for its respective argname. # 參數值:只有一個argnames,argvalues則是值列表。有N個argnames時,每個元祖對應一組argnames,所有元祖組合成一個列表 :arg indirect: The list of argnames or boolean. A list of arguments' names (self,subset of argnames). If True the list contains all names from the argnames. Each argvalue corresponding to an argname in this list will be passed as request.param to its respective argname fixture function so that it can perform more expensive setups during the setup phase of a test rather than at collection time. # indirect:當indirect=True時,若傳入的argnames是fixture函數名,此時fixture函數名將成為一個可執行的函數, argvalues作為fixture的參數,執行fixture函數,最終結果再存入 request.param; 當indirect=False時,fixture函數只作為一個參數名給測試收集階段調用。 # 什么是 the setup phase 測試設置階段? 理解為配置 conftest.py 階段 # 什么是 the collection phase 測試收集階段? 理解為 用例執行 階段 :arg ids: list of string ids, or a callable. If strings, each is corresponding to the argvalues so that they are part of the test id. If None is given as id of specific test, the automatically generated id for that argument will be used. If callable, it should take one argument (self,a single argvalue) and return a string or return None. If None, the automatically generated id for that argument will be used. If no ids are provided they will be generated automatically from the argvalues. # ids:字符串列表,可以理解成標題,與用例個數保持一致 :arg scope: if specified it denotes the scope of the parameters. The scope is used for grouping tests by parameter instances. It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. # 如果指定,則表示參數的范圍。作用域用於按參數實例對測試進行分組。它還將覆蓋任何fixture函數定義的范圍,允許使用測試上下文或配置設置動態范圍。 """
使用parametrize中ids的用法:
1)以除法為例(ids 用例標題,與 argvalues 列表長度一致):
import pytest def division(a, b): return int(a / b) @pytest.mark.parametrize('a, b, c', [(4, 2, 2), (0, 2, 0), (1, 0, 0), (6, 8, 0)], ids=['整除', '被除數為0', '除數為0', '非整除']) def test_1(a, b, c): res = division(a, b) assert res == c if __name__ == '__main__': pytest.main(["-s","test_abc.py"])
使用 pytest.mark.parametrize 參數化的時候,加 ids 參數用例描述有中文時,在控制台輸出會顯示unicode編碼,中文不能正常顯示。
使用 pytest_collection_modifyitems 鈎子函數,對輸出的 item.name 和 item.nodeid 重新編碼。
pytest_collection_modifyitems
在項目的根目錄寫個 conftest.py 文件,加以下代碼
def pytest_collection_modifyitems(items): """ 測試用例收集完成時,將收集到的item的name和nodeid的中文顯示在控制台上 :return: """ for item in items: item.name = item.name.encode("utf-8").decode("unicode_escape") print(item.nodeid) item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
重新運行,查看結果
或者在pytest.ini文件里添加下面一行代碼:
[pytest] disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
以上兩種方式都好使
2)parametrize 還可以疊加使用(順便看看疊加后,都加上 ids 會有什么效果):
import pytest def division(a, b): return int(a / b) @pytest.mark.parametrize('a, b, c', [(4, 2, 2), (0, 2, 0), (1, 0, 0), (6, 8, 0)], ids=['整除', '被除數為0', '除數為0', '非整除']) def test_1(a, b, c): res = division(a, b) assert res == c @pytest.mark.parametrize('a', [100,75], ids=['1疊加parametrize','3疊加parametrize']) @pytest.mark.parametrize('b, c', [(4,25),(3,25)], ids=['2疊加parametrize','4疊加parametrize']) def test_1(a, b, c): res = division(a, b) assert res == c
執行結果
============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.0.2, py-1.9.0, pluggy-0.13.1 rootdir: E:\PycharmProjects\lianxi, configfile: pytest.ini plugins: forked-1.3.0, html-1.22.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 test_abc.py::test_1[2\u53e0\u52a0parametrize-1\u53e0\u52a0parametrize] test_abc.py::test_1[2\u53e0\u52a0parametrize-3\u53e0\u52a0parametrize] test_abc.py::test_1[4\u53e0\u52a0parametrize-1\u53e0\u52a0parametrize] test_abc.py::test_1[4\u53e0\u52a0parametrize-3\u53e0\u52a0parametrize] collected 4 items test_abc.py .FF. ================================== FAILURES =================================== ____________________ test_1[2疊加parametrize-3疊加parametrize] ____________________ a = 75, b = 4, c = 25 @pytest.mark.parametrize('a', [100,75], ids=['1疊加parametrize','3疊加parametrize']) @pytest.mark.parametrize('b, c', [(4,25),(3,25)], ids=['2疊加parametrize','4疊加parametrize']) def test_1(a, b, c): res = division(a, b) > assert res == c E assert 18 == 25 test_abc.py:286: AssertionError ____________________ test_1[4疊加parametrize-1疊加parametrize] ____________________ a = 100, b = 3, c = 25 @pytest.mark.parametrize('a', [100,75], ids=['1疊加parametrize','3疊加parametrize']) @pytest.mark.parametrize('b, c', [(4,25),(3,25)], ids=['2疊加parametrize','4疊加parametrize']) def test_1(a, b, c): res = division(a, b) > assert res == c E assert 33 == 25 test_abc.py:286: AssertionError ------ generated html file: file://E:\PycharmProjects\lianxi\report.html ------ =========================== short test summary info =========================== FAILED test_abc.py::test_1[2疊加parametrize-3疊加parametrize] - assert 18 == 25 FAILED test_abc.py::test_1[4疊加parametrize-1疊加parametrize] - assert 33 == 25 ========================= 2 failed, 2 passed in 0.13s =========================
由上可得結論
1.疊加使用和不疊加使用同時存在時,優先疊加使用 2.疊加后 ids 也會疊加 3.疊加后,顯示順序是下面的疊加在上面的前面 4.參數執行順序是(a=100,b=4,c=25),(a=75,b=4,c=25),(a=100,b=3,c=25),(a=75,b=3,c=25)