陸陸續(xù)續(xù)斷更好久好久了,這么久發(fā)生了很多事情清女,也思考了很多事情钱烟。突然發(fā)現(xiàn)拖延癥已經(jīng)嚴(yán)重影響到了我。
什么是拖延癥嫡丙,簡單來說就是個(gè)人選擇而已拴袭。每時(shí)每刻,面臨辛苦的選項(xiàng)與逃避的選項(xiàng)時(shí)曙博,傾向于選擇更安逸的那一個(gè)拥刻。一而再再而三,就成了拖延父泳。很早之前我就計(jì)劃專注某個(gè)方面寫一個(gè)系列般哼,但是直到今日,仍沒有結(jié)果惠窄,實(shí)在是有些慚愧蒸眠,想從新逼迫自己進(jìn)步,不知道能堅(jiān)持多久杆融。
言歸正傳楞卡,談下pytest,很多人會(huì)有疑問,網(wǎng)上都那么多pytest文章了臀晃,為什么我還要專門寫呢觉渴,其實(shí)很簡單。原因有二徽惋,第一個(gè)就是老生常談的話題了多數(shù)人寫技術(shù)博客都是通過寫來看自己掌握的進(jìn)度案淋,同時(shí)哪天有用到了,回過頭看险绘,也可以起到溫故而知新的作用踢京。第二,剛好有測試妹子給我提供了一些簡單的pytest的小案例宦棺,我也正有此意瓣距,那這篇文章就這樣來了。
先聲明:我寫的技術(shù)文主要還是以理解為主代咸,不一定專業(yè)蹈丸,如果看完還是不會(huì),那一定是我寫的不夠好呐芥。不要因?yàn)槲覍懙奶^于乏味而打消自己學(xué)習(xí)的念頭逻杖。
回到正文pytest
,可能很多常寫python
的人第一次聽到這個(gè)庫思瘟,它究竟有什么用呢荸百?
pytest 是一個(gè)成熟的全功能的 Python 測試工具。這里提供一份Pytest 使用手冊以及官方文檔滨攻。
- 手冊:https://learning-pytest.readthedocs.io/zh/latest/
- 官方文檔:https://docs.pytest.org/en/latest/contents.html
pytest的特點(diǎn)
- 簡單靈活够话,容易上手,文檔豐富光绕;
- 支持參數(shù)化女嘲,可以細(xì)粒度地控制要測試的測試用例;
- 能夠支持簡單的單元測試和復(fù)雜的功能測試诞帐,還可以用來做
selenium/appnium
等自動(dòng)化測試澡为、接口自動(dòng)化測試(pytest
+requests
);pytest
具有很多第三方插件,并且可以自定義擴(kuò)展景埃,比較好用的如pytest-selenium
(集成selenium)媒至、pytest-html
(完美html測試報(bào)告生成)、pytest-rerunfailures
(失敗case重復(fù)執(zhí)行)谷徙、pytest-xdist
(多CPU分發(fā))等拒啰;- 測試用例的
skip
和xfail
處理;- 可以很好的和CI工具結(jié)合完慧,例如
jenkins
等等谋旦;
安裝
- pip安裝
pip install -U pytest
- 驗(yàn)證安裝
pytest --version # 會(huì)展示當(dāng)前已安裝版本
- 在
pytest
中,assert
是編寫測試的最基礎(chǔ)工具。如:
a = 1
b = 3
assert a == b
執(zhí)行結(jié)果
Traceback (most recent call last):
File "/home/xsl/test.py", line 3, in <module>
assert a == b
AssertionError
Pytest 查找測試策略
默認(rèn)情況下册着,pytest
會(huì)遞歸查找當(dāng)前目錄下所有以 test
開始或結(jié)尾的 Python
腳本拴孤,并執(zhí)行文件內(nèi)的所有以 test
開始或結(jié)束的函數(shù)和方法。
# test_no_mark.py
def test_func1():
assert 1 == 1
def test_func2():
assert 1 != 1
所以甲捏,編寫pytest
測試樣例非常簡單演熟,只需要按照下面的規(guī)則:
- 測試文件以
test_
開頭(以_test
結(jié)尾也可以) - 測試類以
Test
開頭,并且不能帶有init
方法(注意:定義class時(shí)司顿,需要以T開頭芒粹,不然pytest是不會(huì)去運(yùn)行該class的) - 測試函數(shù)以
test_
開頭
僅用四行代碼創(chuàng)建一個(gè)簡單的測試函數(shù) 文件名為test.py
import pytest
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
if __name__ == '__main__':
pytest.main("-s test.py")
執(zhí)行測試代碼結(jié)果如下,pytest將顯示錯(cuò)誤信息大溜,因?yàn)閒unc(3)未返回5:
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0
rootdir: /home/xsl, inifile:
collected 1 item
test.py F
=================================== FAILURES ===================================
_________________________________ test_answer __________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test.py:15: AssertionError
=============================== warnings summary ===============================
None
passing a string to pytest.main() is deprecated, pass a list of arguments instead.
-- Docs: http://doc.pytest.org/en/latest/warnings.html
===================== 1 failed, 1 warnings in 0.10 seconds =====================
- 因?yàn)槊米佑玫降氖?code>@pytest.mark.parametrize化漆,那我們就詳細(xì)講解下pytest.mark.parametrize
使用方法:@pytest.mark.parametrize('參數(shù)',list)
參數(shù)一是字符串钦奋,多個(gè)參數(shù)中間用逗號(hào)隔開
第二個(gè)參數(shù)是list座云,多組數(shù)據(jù)用元祖類型;list的每個(gè)元素都是一個(gè)元組付材,元組里的每個(gè)元素和按參數(shù)順序一一對(duì)應(yīng)
# 一個(gè)參數(shù)
@pytest.mark.parametrize('參數(shù)名'朦拖,list) 進(jìn)行參數(shù)化
# 兩個(gè)參數(shù)
@pytest.mark.parametrize('參數(shù)名1,參數(shù)名2'伞租,[(參數(shù)1_data[0], 參數(shù)2_data[0]), (參數(shù)1_data[1], 參數(shù)2_data[1])])
- 小試牛刀
import pytest
data =[[1,1], [2,1+1], [6,2*3],]
@pytest.mark.parametrize('a, b', data)
def test_assert(a, b):
assert a == b
if __name__ == '__main__':
pytest.main('-q test.py')
執(zhí)行結(jié)果
... [100%]
=============================== warnings summary ===============================
None
passing a string to pytest.main() is deprecated, pass a list of arguments instead.
-- Docs: http://doc.pytest.org/en/latest/warnings.html
3 passed, 1 warnings in 0.01 seconds
- 傳入多個(gè)參數(shù)
import pytest
@pytest.mark.parametrize("user_nm, pass_wd",
[("xu", "123456"), ("ni", "123456"),
("xing", "123456"), ("chen", "123456")])
def test_login(user_nm, pass_wd):
print(user_nm + " : " + pass_wd)
assert pass_wd == '123456'
if __name__ == '__main__':
pytest.main('-s test.py')
執(zhí)行結(jié)果如下:
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0
rootdir: /home/xsl/gogs/dps/etl, inifile:
collected 4 items
test.py xu : 123456
.ni : 123456
.xing : 123456
.chen : 123456
.
=============================== warnings summary ===============================
None
passing a string to pytest.main() is deprecated, pass a list of arguments instead.
-- Docs: http://doc.pytest.org/en/latest/warnings.html
===================== 4 passed, 1 warnings in 0.01 seconds =====================
細(xì)心的朋友發(fā)現(xiàn)了,每次我執(zhí)行的時(shí)候會(huì)用pytest.main('-s 文件名')
其實(shí)這里的-s是可以根據(jù)不同的需求進(jìn)行替換的限佩,這里我們替換成-v葵诈,那么執(zhí)行結(jié)果就變成了
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0 -- /usr/bin/python3.5
cachedir: .pytest_cache
rootdir: /home/xsl, inifile:
collecting ... collected 4 items
test.py::test_login[xu-123456] PASSED [ 25%]
test.py::test_login[ni-123456] PASSED [ 50%]
test.py::test_login[xing-123456] PASSED [ 75%]
test.py::test_login[chen-123456] PASSED [100%]
=============================== warnings summary ===============================
None
passing a string to pytest.main() is deprecated, pass a list of arguments instead.
-- Docs: http://doc.pytest.org/en/latest/warnings.html
===================== 4 passed, 1 warnings in 0.01 seconds =====================
- -s: 顯示程序中的print/logging輸出
- -v: 豐富信息模式, 輸出更詳細(xì)的用例執(zhí)行信息
- -q: 安靜模式, 不輸出環(huán)境信息
- -k:關(guān)鍵字匹配,用and區(qū)分:匹配范圍(文件名祟同、類名作喘、函數(shù)名)
- -x:出現(xiàn)一條測試用例失敗就退出測試。在調(diào)試階段非常有用晕城,當(dāng)測試用例失敗時(shí)泞坦,應(yīng)該先調(diào)試通過,而不是繼續(xù)執(zhí)行測試用例砖顷。
這樣就可以寫很多個(gè)測試文件贰锁,然后在另外的文件中使用pytest.main('-s 文件名')
去執(zhí)行,又因?yàn)榍拔闹v了滤蝠,pytest
會(huì)遞歸查找當(dāng)前目錄下所有以 test 開始或結(jié)尾的 Python 腳本豌熄,并執(zhí)行文件內(nèi)的所有以 test開始或結(jié)束的函數(shù)和方法。所以當(dāng)需要測試的文件特別多物咳,就可以直接另寫一個(gè)文件
import pytest
if __name__ == '__main__':
pytest.main('-s test.py')
其實(shí)根據(jù)自己不同的需求來確定pytest.main('')
括號(hào)內(nèi)的內(nèi)容锣险,比如
- 運(yùn)行
./
目錄下所有(test_*.py
和*_test.py
)
pytest.main(['./'])
- 運(yùn)行
./xdj
目錄下的所有用例
pytest.main (['./xdj'])
- 運(yùn)行指定模塊
pytest.main (['./xdj/test01.py'])
- 運(yùn)行
test01.py
中的test_login
方法
pytest.main (['./xdj/test01.py::test_login'])
- 運(yùn)行
test02.py
中test2
(類)的方法test_class
pytest.main (['./xdj/test02.py::test2::test_class'])
- 匹配包含
love
的用例(匹配目錄名、模塊名、類名芯肤、用例名)
pytest.main (['-k','love'])
- 匹配
test.py
模塊下包含hello
的用例
pytest.main(['-k','hello','./xdj/test.py'])