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