pytest 是什么娇斩?
pytest是python中非常流行的ut測試框架盖喷,這個框架有多流行呢夯秃? 應(yīng)該說現(xiàn)在比較新的開源項目基本都已經(jīng)使用pytest用來寫UT了愤惰,比如我們熟悉的requests,ansible锨推,flask铅歼, pypy
等庫有興趣的同學(xué)不妨去Github上看下對應(yīng)的test目錄下的測試文件,全部都是用pytest作為UT測試框架的换可。而且Pycharm椎椰,VSCode等IDE已經(jīng)原生支持這個框架,只要在設(shè)置中將默認框架從unittest切換為pytest即可沾鳄。并且這個框架在用戶群體中也有非常高的評價慨飘,比如:
當(dāng)然這個框架除了作為ut測試框架以外,用來做BDD或者E2E測試也是完全沒有問題的译荞。比如接口測試框架HttpRunner3.0就是直接包了一層pytest外加一些拓展實現(xiàn)的瓤的。
為什么pytest如此流行
筆者自己的經(jīng)歷來說,在接觸pytest之前曾經(jīng)在cucumber吞歼,behave圈膏,robotframework
之間徘徊不定。直到發(fā)現(xiàn)了pytest這個框架以后篙骡,感覺就是duang~ 用起來各種流暢稽坤。下面來簡單介紹框架提供的幾個核心特點:
-
用例寫作非常簡練,沒有模版代碼糯俗,非常pythonic慎皱。寫作一個簡單的用例跟普通的代碼相差無幾,例如:
def test_simple_case(): b = [2, 3, 4] assert 1 in b
運行測試只需要在同級目錄下輸入:
$ pytest
-
Assert重寫叶骨,更加友好的錯誤信息,定位問題一目了然:
pytest
重載了默認的assert方法祈匙,你可以像書寫普通的條件語句一樣書寫你的assert方法忽刽。再也不用記住其他框架里諸如AssertEqual, AssertContains
這些關(guān)鍵字了天揖。并且在遇到Assertion
失敗的時候,提供的上下文也非常簡潔明了跪帝。比如l下面的assertion錯誤信息今膊,可以非常直觀的看到出錯的地方在 index100的地方:def test_eq_list(): long_list = [0, 1, 2] * 100 a = long_list + [3, 4, 5] b = long_list + [4, 4, 5] > assert a == b E assert [0, 1, 2, 0, 1, 2, ...] == [0, 1, 2, 0, 1, 2, ...] E At index 300 diff: 3 != 4 E Use -v to get the full diff
-
Mark(類似其他框架里面的標簽)功能:
Mark用來標記測試用例,用來方便的為測試用例添加屬性信息伞剑,以方便插件根據(jù)這些熟悉信息作一些特殊處理斑唬。比如內(nèi)置的
skip, skipif, xfail
等mark,就分別用來表示跳過黎泣,條件式跳過恕刘,預(yù)期內(nèi)的失敗用例。比如下面的例子抒倚,可以標記某些case在windows的環(huán)境上跳過執(zhí)行等:import sys import pytest def test_normal_case(): """no skip mark function""" pass @pytest.mark.skip(reason="not implemented yet") def test_in_the_future(): raise NotImplemented # class-level mark @pytest.mark.skipif(sys.platform == 'win32', reason="not applicable on windows x86") class TestPosixCalls(object): def test_only_available_in_linux(self): """will not be setup or run under 'win32' platform"""
而一些其他插件比如pytest-repeat褐着, pytest-timeout等,則分別使用
@pytest.mark.repeat()
和@pytest.mark.timeout()
來單獨標記某些需要重復(fù)執(zhí)行多次的用例以及用例執(zhí)行執(zhí)行超時控制(超時則停止執(zhí)行并且標記為失斖信弧)含蓉。 -
Fixture(類似其他框架里的setup/teardown,依賴注入项郊,共享實例等)系統(tǒng):
-
使用
yield
聲明測試前與測試后執(zhí)行代碼馅扣,語法非常簡潔,例如:@pytest.fixture() def db_session(scope="session"): db = DBSession('scratch') db.connect() yield db db.close() def test_query_data(db_session): # do something with db_session here assert pass def test_update_data(db_session): # do something with db_session here assert pass
這里將
db_session
聲明為一個fixture
着降,并且作為參數(shù)傳入測試用例差油,用例就可以直接基于這個db_session
做操作。無需關(guān)心具體的數(shù)據(jù)庫連接和斷開時機鹊碍。由于fixutre
是支持function厌殉,class,module侈咕,session
不同層級的共享公罕,這里我們將scope設(shè)置為session,意思是這一輪測試里面耀销,db.connec
t和db.close()
方便按需進行實例化: -
支持參數(shù)化用例聲明楼眷,例如:
import pytest @pytest.mark.parametrize("test_input,expected", [ ("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail), ]) def test_eval(test_input, expected): assert eval(test_input) == expected
在實際執(zhí)行時會得到類似如下的輸出,也就是同樣的步驟在不同的輸入情況下的輸出用例:
collected 3 items fixture/test_parametrize.py::test_eval[3+5-8] PASSED [ 33%] fixture/test_parametrize.py::test_eval[2+4-6] PASSED [ 66%] fixture/test_parametrize.py::test_eval[6*9-42] XFAIL [100%]
-
-
插件系統(tǒng)
作為測試框架熊尉,pytest有著非常好的插件系統(tǒng)設(shè)計罐柳。框架本身基于pluggy框架開發(fā)狰住,通過調(diào)用定義良好的hooks來實現(xiàn)配置张吉,收集,執(zhí)行和報告這些過程催植。官方收錄的插件列表就有300+肮蛹,并且插件開發(fā)也比較容易上手勺择,只需要了解一下框架提供的幾個hook和一些類的屬性,有興趣的同學(xué)也可以看下筆者的個人博客的介紹伦忠。
注:有些內(nèi)容因為截圖比較多省核,大家可以結(jié)合附件的PPT一起看。這里就不再贅述了昆码。限于篇幅以及筆者水平气忠,還有一些高級的功能無法給到家娓娓道來,有興趣的同學(xué)可以直接去官網(wǎng)看教程赋咽。
總的來說旧噪,筆者的感慨就是好的框架想要走的長遠都需要在三個點上花功夫:
簡單: 這個很大程度上決定你的受眾有多少,也就是你能走多快的問題冬耿;
可靠:我們當(dāng)然不會選擇一個簡單易出錯或者一拓展就懵逼的這種框架舌菜,可靠的框架會在使用中不斷積累用戶口碑,吸引更多用戶加入亦镶。
易拓展:這個基本上決定你能走多遠日月,沒有拓展性的框架一般壽命不會長久,很快就會被新的框架替代缤骨。pytest正是借助其良好的插件系統(tǒng)設(shè)計爱咬,拓展了許許多多非常實用的功能。畢竟绊起,沒人人比用戶更懂自己的需求(喬布斯除外)精拟。