做it的在哪個網(wǎng)站找工作百度注冊公司網(wǎng)站
上篇博文介紹過,Pytest是目前比較成熟功能齊全的測試框架,使用率肯定也不斷攀升。
在實際工作中,許多測試用例都是類似的重復(fù),一個個寫最后代碼會顯得很冗余。這里,我們來了解一下@pytest.mark.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列表向基礎(chǔ)測試函數(shù)添加新的調(diào)用,在收集階段執(zhí)行參數(shù)化。
-
:arg argnames: a comma-separated string denoting one or more argument
-
names, or a list/tuple of argument strings. # 參數(shù)名:使用逗號分隔的字符串,列表或元祖,表示一個或多個參數(shù)名
-
: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. # 參數(shù)值:只有一個argnames,argvalues則是值列表。有N個argnames時,每個元祖對應(yīng)一組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.
-
: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:字符串列表,可以理解成標題,與用例個數(shù)保持一致
-
: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.
-
# 如果指定,則表示參數(shù)的范圍。作用域用于按參數(shù)實例對測試進行分組。
-
它還將覆蓋任何fixture函數(shù)定義的范圍,允許使用測試上下文或配置設(shè)置動態(tài)范圍。
-
"""
argnames
釋義:參數(shù)名稱。
格式:字符串"arg1,arg2,arg3"。
aegvalues
釋義:參數(shù)值列表。
格式:必須是列表,如[val1,val2,val3]。
-
單個參數(shù),里面是值的列表,如@pytest.mark.parametrize("name",["Jack","Locus","Bill"]);
-
多個參數(shù),需要用元祖來存放值,一個元祖對應(yīng)一組參數(shù)的值,如@pytest.mark.parametrize("user,age",[("user1",15),("user2",24),("user3",25)])。
ids
釋義:可以理解為用例的id。
格式:字符串列表,如["case1","case2","case3"]。
indirect
釋義:當indirect=True時,若傳入的argnames是fixture函數(shù)名,此時fixture函數(shù)名將成為一個可執(zhí)行的函數(shù),argvalues作為fixture的參數(shù),執(zhí)行fixture函數(shù),最終結(jié)果再存入request.param。
當indirect=False時,fixture函數(shù)只作為一個參數(shù)名給測試收集階段調(diào)用。
備注:這里可以將the setup phase(測試設(shè)置階段)理解為配置 conftest.py 階段,將the collection phase(測試收集階段)理解為用例執(zhí)行階段。
裝飾測試類
-
import pytest
-
data = [
-
(2,2,4),
-
(3,4,12)
-
]
-
def add(a,b):
-
return a * b
-
@pytest.mark.parametrize('a,b,expect',data)
-
class TestParametrize(object):
-
def test_parametrize_1(self,a,b,expect):
-
print('\n測試函數(shù)1測試數(shù)據(jù)為\n{}-{}'.format(a,b))
-
assert add(a,b) == expect
-
def test_parametrize_2(self,a,b,expect):
-
print('\n測試函數(shù)2測試數(shù)據(jù)為\n{}-{}'.format(a,b))
-
assert add(a,b) == expect
-
if __name__ == "__main__":
-
pytest.main(["-s","test_07.py"])
-
============================= test session starts =============================
-
platform win32 -- Python 3.8.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
-
rootdir: D:\AutoCode
-
plugins: html-3.1.1, metadata-1.11.0
-
collecting ... collected 4 items
-
test_07.py::TestParametrize::test_parametrize_1[2-2-4]
-
測試函數(shù)1測試數(shù)據(jù)為
-
2-2
-
PASSED
-
test_07.py::TestParametrize::test_parametrize_1[3-4-12]
-
測試函數(shù)1測試數(shù)據(jù)為
-
3-4
-
PASSED
-
test_07.py::TestParametrize::test_parametrize_2[2-2-4]
-
測試函數(shù)2測試數(shù)據(jù)為
-
2-2
-
PASSED
-
test_07.py::TestParametrize::test_parametrize_2[3-4-12]
-
測試函數(shù)2測試數(shù)據(jù)為
-
3-4
-
PASSED
-
============================== 4 passed in 0.12s ==============================
-
Process finished with exit code 0
由以上代碼可以看到,當裝飾器裝飾測試類時,定義的數(shù)據(jù)集合會被傳遞給類的所有方法。
裝飾測試函數(shù)
單個數(shù)據(jù)
-
import pytest
-
data = ["Rose","white"]
-
@pytest.mark.parametrize("name",data)
-
def test_parametrize(name):
-
print('\n列表中的名字為\n{}'.format(name))
-
if __name__ == "__main__":
-
pytest.main(["-s","test_07.py"])
-
============================= test session starts =============================
-
platform win32 -- Python 3.8.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
-
rootdir: D:\AutoCode
-
plugins: html-3.1.1, metadata-1.11.0
-
collected 2 items
-
test_07.py
-
列表中的名字為
-
Rose
-
.
-
列表中的名字為
-
white
-
.
-
============================== 2 passed in 0.09s ==============================
-
Process finished with exit code 0
當測試用例只需要一個參數(shù)時,我們存放數(shù)據(jù)的列表無序嵌套序列,@pytest.mark.parametrize("name", data)?裝飾器的第一個參數(shù)也只需要一個變量接收列表中的每個元素,第二個參數(shù)傳遞存儲數(shù)據(jù)的列表,那么測試用例需要使用同名的字符串接收測試數(shù)據(jù)(實例中的name)且列表有多少個元素就會生成并執(zhí)行多少個測試用例。
一組數(shù)據(jù)
-
import pytest
-
data = [
-
[1, 2, 3],
-
[4, 5, 9]
-
] # 列表嵌套列表
-
# data_tuple = [
-
# (1, 2, 3),
-
# (4, 5, 9)
-
# ] # 列表嵌套元組
-
@pytest.mark.parametrize('a, b, expect', data)
-
def test_parametrize_1(a, b, expect): # 一個參數(shù)接收一個數(shù)據(jù)
-
print('\n測試數(shù)據(jù)為\n{},{},{}'.format(a, b, expect))
-
actual = a + b
-
assert actual == expect
-
@pytest.mark.parametrize('value', data)
-
def test_parametrize_2(value): # 一個參數(shù)接收一組數(shù)據(jù)
-
print('\n測試數(shù)據(jù)為\n{}'.format(value))
-
actual = value[0] + value[1]
-
assert actual == value[2]
-
if __name__ == "__main__":
-
pytest.main(["-s","test_07.py"])
-
============================= test session starts =============================
-
platform win32 -- Python 3.8.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
-
rootdir: D:\AutoCode
-
plugins: html-3.1.1, metadata-1.11.0
-
collected 4 items
-
test_07.py
-
測試數(shù)據(jù)為
-
1,2,3
-
.
-
測試數(shù)據(jù)為
-
4,5,9
-
.
-
測試數(shù)據(jù)為
-
[1, 2, 3]
-
.
-
測試數(shù)據(jù)為
-
[4, 5, 9]
-
.
-
============================== 4 passed in 0.09s ==============================
-
Process finished with exit code 0
當測試用例需要多個數(shù)據(jù)時,我們可以使用嵌套序列(嵌套元組&嵌套列表)的列表來存放測試數(shù)據(jù)。
裝飾器@pytest.mark.parametrize()可以使用單個變量接收數(shù)據(jù),也可以使用多個變量接收,同樣,測試用例函數(shù)也需要與其保持一致。
當使用單個變量接收時,測試數(shù)據(jù)傳遞到測試函數(shù)內(nèi)部時為列表中的每一個元素或者小列表,需要使用索引的方式取得每個數(shù)據(jù)。當使用多個變量接收數(shù)據(jù)時,那么每個變量分別接收小列表或元組中的每個元素列表嵌套多少個多組小列表或元組,測生成多少條測試用例。
組合數(shù)據(jù)
-
import pytest
-
data_1 = [1,2,3]
-
data_2 = ['a','b']
-
@pytest.mark.parametrize('a',data_1)
-
@pytest.mark.parametrize('b',data_2)
-
def test_parametrize_1(a,b):
-
print(f'笛卡爾積測試結(jié)果為:{a},')
-
if __name__ == '__main__':
-
pytest.main(["-vs","test_06.py"])
?通過測試結(jié)果,我們不難分析,一個測試函數(shù)還可以同時被多個參數(shù)化裝飾器裝飾,那么多個裝飾器中的數(shù)據(jù)會進行交叉組合的方式傳遞給測試函數(shù),進而生成n * n個測試用例。
標記用例
-
import pytest
-
@pytest.mark.parametrize("test_input,expected",[
-
("3+5",8),
-
("2+4",6),
-
pytest.param("6 * 9",42,marks=pytest.mark.xfail),
-
pytest.param("6 * 6",42,marks=pytest.mark.skip)
-
])
-
def test_mark(test_input,expected):
-
assert eval(test_input) == expected
-
if __name__ == '__main__':
-
pytest.main(["-vs","test_06.py"])
?輸出結(jié)果顯示收集到4個用例,兩個通過,一個被跳過,一個標記失敗:
-
當我們不想執(zhí)行某組測試數(shù)據(jù)時,我們可以標記skip或skipif;
-
當我們預(yù)期某組數(shù)據(jù)會執(zhí)行失敗時,我們可以標記為xfail等。
嵌套字典
-
import pytest
-
data = (
-
{
-
'user': "name1",
-
'pwd': 123
-
},
-
{
-
'user': "name2",
-
'pwd': 456
-
}
-
)
-
@pytest.mark.parametrize('dic',data)
-
def test_parametrize(dic):
-
print('\n測試數(shù)據(jù)為\n{}'.format(dic))
-
if __name__ == '__main__':
-
pytest.main(["-vs","test_06.py"])
增加測試結(jié)果可讀性
參數(shù)化裝飾器有一個額外的參數(shù)ids,可以標識每一個測試用例,自定義測試數(shù)據(jù)結(jié)果的顯示,為了增加可讀性,我們可以標記每一個測試用例使用的測試數(shù)據(jù)是什么,適當?shù)脑黾右恍┱f明。
在使用前你需要知道,ids參數(shù)應(yīng)該是一個字符串列表,必須和數(shù)據(jù)對象列表的長度保持一致。
-
import pytest
-
data_1 = [
-
(1, 2, 3),
-
(4, 5, 9)
-
]
-
ids = ["a:{} + b:{} = expect:{}".format(a, b, expect) for a, b, expect in data_1]
-
def add(a, b):
-
return a + b
-
@pytest.mark.parametrize('a, b, expect', data_1, ids=ids)
-
class TestParametrize(object):
-
def test_parametrize_1(self, a, b, expect):
-
print('\n測試函數(shù)1測試數(shù)據(jù)為\n{}-{}'.format(a, b))
-
assert add(a, b) == expect
-
def test_parametrize_2(self, a, b, expect):
-
print('\n測試函數(shù)2數(shù)據(jù)為\n{}-{}'.format(a, b))
-
assert add(a, b) == expect
-
if __name__ == '__main__':
-
pytest.main(["-v","test_06.py"])
-
不加ids參數(shù)的返回結(jié)果:
-
加ids參數(shù)的返回結(jié)果:
我們可以看到帶ids參數(shù)的返回結(jié)果中的用例都被一個列表明確的標記了,而且通過這種標記可以更加直觀的看出來,每個測試用例使用的數(shù)據(jù)名稱及測試內(nèi)容。
行動吧,在路上總比一直觀望的要好,未來的你肯定會感 謝現(xiàn)在拼搏的自己!如果想學(xué)習(xí)提升找不到資料,沒人答疑解惑時,請及時加入扣群: 320231853,里面有各種軟件測試+開發(fā)資料和技術(shù)可以一起交流學(xué)習(xí)哦。
最后感謝每一個認真閱讀我文章的人,禮尚往來總是要有的,雖然不是什么很值錢的東西,如果你用得到的話可以直接拿走:
?
這些資料,對于【軟件測試】的朋友來說應(yīng)該是最全面最完整的備戰(zhàn)倉庫,這個倉庫也陪伴上萬個測試工程師們走過最艱難的路程,希望也能幫助到你!