1.Pytest安裝和介紹
1.1 Pytest介紹
pytest是python的一種單元測(cè)試框架镀娶,同自帶的Unittest測(cè)試框架類(lèi)似梯码,相比于Unittest框架使用起來(lái)更簡(jiǎn)潔轩娶,效率更高。
Pytest is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library. `
1.2 Pytes安裝
1.安裝包安裝
1.進(jìn)入下載包路徑
2.python setup install
3.安裝出現(xiàn)權(quán)限問(wèn)題:
3.1.mac/linux 添加sudo廓握,運(yùn)行:sudo python setup install
3.2.windows 管理員方式運(yùn)行cmd窗口隙券,運(yùn)行:python setup install
2.命令行安裝
1.mac/linux:sudo pip3 install -U pytest # -U:可以理解為--upgrade闹司,表示已安裝就升級(jí)為最新版本
2.管理員方式運(yùn)行cmd:pip3 install -U pytest
2.安裝成功校驗(yàn):
1.進(jìn)入命令行
2.運(yùn)行:pytest --version # 會(huì)展示當(dāng)前已安裝版本
1.3 Pytest文檔
存在手機(jī)里游桩,沒(méi)事就拿出來(lái)讀一讀,看一看.說(shuō)實(shí)話(huà),我的英文能力很渣,學(xué)這個(gè)東西感覺(jué)很難,不過(guò)一切慢慢來(lái)吧.為了一份好的工作,為了技術(shù)的提升,我給自己的目標(biāo)是:一周寫(xiě)出一小篇文章.必須有深度,讓別人能看懂,轉(zhuǎn)行真的不容易.記住:有時(shí)間多看看文檔,畢竟了解一些先進(jìn)的技術(shù)是必須看英文文檔的.
1.4 Pytest運(yùn)行方式
# file_name: test_abc.py
import pytest # 引入pytest包
def test_a(): # test開(kāi)頭的測(cè)試函數(shù)
print("------->test_a")
assert 1 # 斷言成功
def test_b():
print("------->test_b")
assert 0 # 斷言失敗
if __name__ == '__main__':
pytest.main("-s test_abc.py") # 調(diào)用pytest的main函數(shù)執(zhí)行測(cè)試
1.測(cè)試類(lèi)主函數(shù)模式
pytest.main("-s test_abc.py")
2.命令行模式
pytest 文件路徑/測(cè)試文件名
例如:pytest ./test_abc.py
在pytest框架中筛峭,有如下約束:
所有的單測(cè)文件名都需要滿(mǎn)足test_.py格式或test.py格式影晓。
在單測(cè)文件中檩禾,可以包含test開(kāi)頭的函數(shù)盼产,也可以包含Test開(kāi)頭的類(lèi)戏售。
在單測(cè)類(lèi)中,可以包含一個(gè)或多個(gè)test_開(kāi)頭的函數(shù)芹关。
此時(shí)侥衬,在執(zhí)行pytest命令時(shí)跑芳,會(huì)自動(dòng)從當(dāng)前目錄及子目錄中尋找符合上述約束的測(cè)試函數(shù)來(lái)執(zhí)行博个。
2.Pytest的setup和teardown函數(shù)
1.setup和teardown主要分為:模塊級(jí),類(lèi)級(jí)盆佣,功能級(jí),函數(shù)級(jí)虑灰。
2.存在于測(cè)試類(lèi)內(nèi)部
代碼示例:
- 函數(shù)級(jí)別setup()/teardown()
運(yùn)行于測(cè)試方法的始末穆咐,即:運(yùn)行一次測(cè)試函數(shù)會(huì)運(yùn)行一次setup和teardown
import pytest
class Test_ABC:
# 函數(shù)級(jí)開(kāi)始
def setup(self):
print("------->setup_method")
# 函數(shù)級(jí)結(jié)束
def teardown(self):
print("------->teardown_method")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->setup_method # 第一次 setup()
------->test_a
.
------->teardown_method # 第一次 teardown()
------->setup_method # 第二次 setup()
------->test_b
.
------->teardown_method # 第二次 teardown()
- 2.2.類(lèi)級(jí)別
運(yùn)行于測(cè)試類(lèi)的始末对湃,即:在一個(gè)測(cè)試內(nèi)只運(yùn)行一次setup_class和teardown_class,不關(guān)心測(cè)試類(lèi)內(nèi)有多少個(gè)測(cè)試函數(shù)心傀。
代碼示例:
import pytest
class Test_ABC:
# 測(cè)試類(lèi)級(jí)開(kāi)始
def setup_class(self):
print("------->setup_class")
# 測(cè)試類(lèi)級(jí)結(jié)束
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->setup_class # 第一次 setup_class()
------->test_a
.
------->test_b
F
------->teardown_class # 第一次 teardown_class()
3.Pytest配置文件
pytest的配置文件通常放在測(cè)試目錄下脂男,名稱(chēng)為pytest.ini疆液,命令行運(yùn)行時(shí)會(huì)使用該配置文件中的配置.
#配置pytest命令行運(yùn)行參數(shù)
[pytest]
addopts = -s ... # 空格分隔陕贮,可添加多個(gè)命令行參數(shù) -所有參數(shù)均為插件包的參數(shù)配置測(cè)試搜索的路徑
testpaths = ./scripts # 當(dāng)前目錄下的scripts文件夾 -可自定義
#配置測(cè)試搜索的文件名稱(chēng)
python_files = test*.py
#當(dāng)前目錄下的scripts文件夾下肮之,以test開(kāi)頭戈擒,以.py結(jié)尾的所有文件 -可自定義
配置測(cè)試搜索的測(cè)試類(lèi)名
python_classes = Test_*
#當(dāng)前目錄下的scripts文件夾下筐高,以test開(kāi)頭,以.py結(jié)尾的所有文件中柑土,以Test開(kāi)頭的類(lèi) -可自定義
配置測(cè)試搜索的測(cè)試函數(shù)名
python_functions = test_*
#當(dāng)前目錄下的scripts文件夾下稽屏,以test開(kāi)頭狐榔,以.py結(jié)尾的所有文件中获雕,以Test開(kāi)頭的類(lèi)內(nèi)届案,以test_開(kāi)頭的方法 -可自定義
4 Pytest常用插件
插件列表網(wǎng)址:https://plugincompat.herokuapp.com
包含很多插件包萝玷,大家可依據(jù)工作的需求選擇使用昆婿。
4.1 前置條件:
1.文件路徑:
- Test_App
- - test_abc.py
- - pytest.ini
2.pyetst.ini配置文件內(nèi)容:
[pytest]
# 命令行參數(shù)
addopts = -s
# 搜索文件名
python_files = test_*.py
# 搜索的類(lèi)名
python_classes = Test_*
#搜索的函數(shù)名
python_functions = test_*
4.2 Pytest測(cè)試報(bào)告
通過(guò)命令行方式仓蛆,生成xml/html格式的測(cè)試報(bào)告看疙,存儲(chǔ)于用戶(hù)指定路徑能庆。插件名稱(chēng):pytest-html
使用方法: 命令行格式:pytest --html=用戶(hù)路徑/report.html
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 0 # 斷言失敗```
運(yùn)行方式:
1.修改Test_App/pytest.ini文件搁胆,添加報(bào)告參數(shù)邮绿,即:addopts = -s --html=./report.html
# -s:輸出程序運(yùn)行信息
# --html=./report.html 在當(dāng)前目錄下生成report.html文件
? 若要生成xml文件船逮,可將--html=./report.html 改成 --html=./report.xml
2.命令行進(jìn)入Test_App目錄
3.執(zhí)行命令: pytest
執(zhí)行結(jié)果:
1.在當(dāng)前目錄會(huì)生成assets文件夾和report.html文件
5.pytest的高階用法(一)
前置條件:
1.文件路徑:
Test_App
- - test_abc.py
- - pytest.ini
2.pyetst.ini配置文件內(nèi)容:
[pytest]
命令行參數(shù)
addopts = -s
搜索文件名
python_files = test*.py
搜索的類(lèi)名
python_classes = Test*
搜索的函數(shù)名
python_functions = test_*
5.1pytest之fixture
fixture修飾器來(lái)標(biāo)記固定的工廠(chǎng)函數(shù),在其他函數(shù)挖胃,模塊酱鸭,類(lèi)或整個(gè)工程調(diào)用它時(shí)會(huì)被激活并優(yōu)先執(zhí)行,通常會(huì)被用于完成預(yù)置處理和重復(fù)操作。
方法:fixture(scope="function", params=None, autouse=False, ids=None, name=None)
常用參數(shù):
scope:被標(biāo)記方法的作用域
function" (default):作用于每個(gè)測(cè)試方法抱既,每個(gè)test都運(yùn)行一次
"class":作用于整個(gè)類(lèi)防泵,每個(gè)class的所有test只運(yùn)行一次
"module":作用于整個(gè)模塊捷泞,每個(gè)module的所有test只運(yùn)行一次
"session:作用于整個(gè)session(慎用)寿谴,每個(gè)session只運(yùn)行一次
params:(list類(lèi)型)提供參數(shù)數(shù)據(jù),供調(diào)用標(biāo)記方法的函數(shù)使用
autouse:是否自動(dòng)運(yùn)行,默認(rèn)為False不運(yùn)行拂到,設(shè)置為T(mén)rue自動(dòng)運(yùn)行
5.2fixture第一個(gè)例子(通過(guò)參數(shù)引用)
示例:
class Test_ABC:
@pytest.fixture()
def before(self):
print("------->before")
def test_a(self,before): # ? test_a方法傳入了被fixture標(biāo)識(shí)的函數(shù)兄旬,已變量的形式
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->before # 發(fā)現(xiàn)before會(huì)優(yōu)先于測(cè)試函數(shù)運(yùn)行
------->test_a
.
5.3.fixture第二個(gè)例子(通過(guò)函數(shù)引用)
示例:
import pytest
@pytest.fixture() # fixture標(biāo)記的函數(shù)可以應(yīng)用于測(cè)試類(lèi)外部
def before():
print("------->before")
@pytest.mark.usefixtures("before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->before # 發(fā)現(xiàn)before會(huì)優(yōu)先于測(cè)試類(lèi)運(yùn)行
------->setup
------->test_a
.
5.4.fixture第三個(gè)例子(默認(rèn)設(shè)置為運(yùn)行)
示例:
import pytest
@pytest.fixture(autouse=True) # 設(shè)置為默認(rèn)運(yùn)行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->before # 發(fā)現(xiàn)before自動(dòng)優(yōu)先于測(cè)試類(lèi)運(yùn)行
------->setup
------->test_a
.
5.5.fixture第四個(gè)例子(設(shè)置作用域?yàn)閒unction)
示例:
import pytest
@pytest.fixture(scope='function',autouse=True) # 作用域設(shè)置為function,自動(dòng)運(yùn)行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->before # 運(yùn)行第一次
------->setup
------->test_a
.------->before # 運(yùn)行第二次
------->setup
------->test_b
.
5.6.fixture第五個(gè)例子(設(shè)置作用域?yàn)閏lass)
示例:
import pytest
@pytest.fixture(scope='class',autouse=True) # 作用域設(shè)置為class绪撵,自動(dòng)運(yùn)行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->before # 發(fā)現(xiàn)只運(yùn)行一次
------->setup
------->test_a
.
------->setup
------->test_b
.
5.7.fixture第六個(gè)例子(返回值)
示例一:
import pytest
@pytest.fixture()
def need_data():
return 2 # 返回?cái)?shù)字2
class Test_ABC:
def test_a(self,need_data):
print("------->test_a")
assert need_data != 3 # 拿到返回值做一次斷言
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
test_abc.py
------->test_a
.
``
示例二:
import pytest
@pytest.fixture(params=[1, 2, 3])
def need_data(request): # 傳入?yún)?shù)request 系統(tǒng)封裝參數(shù)
return request.param # 取列表中單個(gè)值,默認(rèn)的取值方式
class Test_ABC:
def test_a(self,need_data):
print("------->test_a")
assert need_data != 3 # 斷言need_data不等于3
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執(zhí)行結(jié)果:
# 可以發(fā)現(xiàn)結(jié)果運(yùn)行了三次
test_abc.py
1
------->test_a
.
2
------->test_a
.
3
------->test_a
F
6.Pytest高階用法(二)
前置條件:
1.文件路徑:
- Test_App
- - test_abc.py
- - pytest.ini
2.pyetst.ini配置文件內(nèi)容:
[pytest]
命令行參數(shù)
addopts = -s
搜索文件名
python_files = test_*.py
搜索的類(lèi)名
python_classes = Test_*
搜索的函數(shù)名
python_functions = test_*
6.1.跳過(guò)測(cè)試函數(shù)
根據(jù)特定的條件改艇,不執(zhí)行標(biāo)識(shí)的測(cè)試函數(shù).
方法:
skipif(condition, reason=None)
參數(shù):
condition:跳過(guò)的條件坟岔,必傳參數(shù)
reason:標(biāo)注原因,必傳參數(shù)
使用方法:
@pytest.mark.skipif(condition, reason="xxx")
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
@pytest.mark.skipif(condition=2>1,reason = "跳過(guò)該函數(shù)") # 跳過(guò)測(cè)試函數(shù)test_b
def test_b(self):
print("------->test_b")
assert 0
執(zhí)行結(jié)果:
test_abc.py
------->setup_class
------->test_a #只執(zhí)行了函數(shù)test_a
.
------->teardown_class
s # 跳過(guò)函數(shù)```
6.2 標(biāo)記為預(yù)期失敗函數(shù)
標(biāo)記測(cè)試函數(shù)為失敗函數(shù)
方法:
xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用參數(shù):
condition:預(yù)期失敗的條件摔桦,必傳參數(shù)
reason:失敗的原因社付,必傳參數(shù)
使用方法:
@pytest.mark.xfail(condition, reason="xx")
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
@pytest.mark.xfail(2 > 1, reason="標(biāo)注為預(yù)期失敗") # 標(biāo)記為預(yù)期失敗函數(shù)test_b
def test_b(self):
print("------->test_b")
assert 0
執(zhí)行結(jié)果:
test_abc.py
------->setup_class
------->test_a
.
------->test_b
------->teardown_class
x # 失敗標(biāo)記
6.3 函數(shù)數(shù)據(jù)參數(shù)化
方便測(cè)試函數(shù)對(duì)測(cè)試屬于的獲取。
方法:
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
常用參數(shù):
argnames:參數(shù)名
argvalues:參數(shù)對(duì)應(yīng)值邻耕,類(lèi)型必須為list
當(dāng)參數(shù)為一個(gè)時(shí)格式:[value]
當(dāng)參數(shù)個(gè)數(shù)大于一個(gè)時(shí)鸥咖,格式為:[(param_value1,param_value2.....),(param_value1,param_value2.....)]
使用方法:
@pytest.mark.parametrize(argnames,argvalues)
? 參數(shù)值為N個(gè),測(cè)試方法就會(huì)運(yùn)行N次
單個(gè)參數(shù)示例:
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參數(shù)被賦予兩個(gè)值兄世,函數(shù)會(huì)運(yùn)行兩遍
def test_a(self,a): # 參數(shù)必須和parametrize里面的參數(shù)一致
print("test data:a=%d"%a)
assert a%3 == 0
執(zhí)行結(jié)果:
test_abc.py
------->setup_class
test data:a=3 # 運(yùn)行第一次取值a=3
.
test data:a=6 # 運(yùn)行第二次取值a=6
.
------->teardown_class
多個(gè)參數(shù)示例:
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)]) # 參數(shù)a,b均被賦予兩個(gè)值,函數(shù)會(huì)運(yùn)行兩遍
def test_a(self,a,b): # 參數(shù)必須和parametrize里面的參數(shù)一致
print("test data:a=%d,b=%d"%(a,b))
assert a+b == 3
執(zhí)行結(jié)果:
test_abc.py
------->setup_class
test data:a=1,b=2 # 運(yùn)行第一次取值 a=1,b=2
.
test data:a=0,b=3 # 運(yùn)行第二次取值 a=0,b=3
.
------->teardown_class
函數(shù)返回值類(lèi)型示例:
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()) # 使用函數(shù)返回值的形式傳入?yún)?shù)值
def test_a(self,a,b):
print("test data:a=%d,b=%d"%(a,b))
assert a+b == 3
執(zhí)行結(jié)果:
test_abc.py
------->setup_class
test data:a=1,b=2 # 運(yùn)行第一次取值 a=1,b=2
.
test data:a=0,b=3 # 運(yùn)行第二次取值 a=0,b=3
.
------->teardown_class