在Python語言系中,有很多可用的自動化測試框架,比如早期大多數(shù)人會選用unittest+HTMLTestRunner、Nose等,最近幾年比較常用的有Robot Framework,Robot Framework它是Python下一款非常通用的測試框架,采用擴展插件的機制可以幫助我們實現(xiàn)幾乎任何類型的自動化測試工作,如接口自動化測試、App自動化測試、Web UI自動化測試等。
今天本文重點介紹在Python語言下,另外一款通用的測試框架Pytest,雖說作為Robot Framework框架一書的作者去介紹Pytest,貌似不太合理,但框架技術本是一家,能快速解決實際問題的框架就是好框架,在年初的時候,也發(fā)表過一篇關于Robot Framework與Pytest框架選擇的一些建議:聊一聊:Robot Framework被誤會多年的秘密,感興趣的讀者可以看看。
一句話總結:Pytest核心思路和Robot Framework大體一樣,可以通過插件擴展的形式,來滿足不同場景下的自動化測試需求。
1. Pytest介紹
Pytest是一個非常成熟的全功能的Python測試框架,與python自帶的unittest測試框架類似,但是比unittest框架使用起來更簡潔,功能更強大。
提供完善的在線文檔,并有著大量的第三方插件和內(nèi)置幫助,適用于許多小型或大型項目。適合簡單的單元測試到復雜的功能測試。還可以執(zhí)行nose,unittest和doctest風格的測試用例。支持良好的集成實踐, 支持擴展的 xUnit 風格 setup,支持非 Python 測試。支持生成測試覆蓋率報告,支持 PEP8 兼容的編碼風格。
2. Pytest安裝及基本使用
Pytest安裝非常簡單,可以通過 pip 命令直接在線安裝:
pipinstall-Upytest
Pytest 官方文檔:https://docs.pytest.org/en/latest/
安裝好之后,調(diào)用 pytest測試腳本方式:
1、py.test:
Pytest 提供直接調(diào)用的命令行工具,即 py.test,最新版本 pytest 和 py.test 兩個命令行工具都可用
2、python -m pytest:
效果和 py.test 一樣, 這種調(diào)用方式在多 Python 版本測試的時候是有用的, 例如測試 Python3:
python3-mpytest[...]
基本語法:
usage:py.test[options][file_or_dir][file_or_dir][...]
部分參數(shù)介紹:
py.test--version查看版本 py.test--fixtures,--funcargs查看可用的fixtures pytest--markers查看可用的markers py.test-h,--help命令行和配置文件幫助 #失敗后停止 py.test-x首次失敗后停止執(zhí)行 py.test--maxfail=2兩次失敗之后停止執(zhí)行 #調(diào)試輸出 py.test-l,--showlocals在traceback中顯示本地變量 py.test-q,--quiet靜默模式輸出 py.test-v,--verbose輸出更詳細的信息 py.test-s捕獲輸出,例如顯示print函數(shù)的輸出 py.test-rchar顯示指定測試類型的額外摘要信息 py.test--tb=style錯誤信息輸出格式 -long默認的traceback信息格式化形式 -native標準庫格式化形式 -short更短的格式 -line每個錯誤一行 #運行指定marker的測試 pytest-mMARKEXPR #運行匹配的測試 py.test-kstringexpr #只收集并顯示可用的測試用例,但不運行測試用例 py.test--collect-only #失敗時調(diào)用PDB py.test--pdb
3.Pytest用例執(zhí)行
3.1 用例查找規(guī)則
如果不帶參數(shù)運行pytest,那么其先從配置文件(pytest.ini,tox.ini,setup.cfg)中查找配置項testpaths指定的路徑中的test case,如果沒有則從當前目錄開始查找,否則,命令行參數(shù)就用于目錄、文件查找。查找的規(guī)則如下:
查找指定目錄中以 test 開頭的目錄
遞歸遍歷目錄,除非目錄指定了不同遞歸
查找文件名以 test_ 開頭的文件
查找以 Test 開頭的類(該類不能有 init 方法)
查找以 test_ 開頭的函數(shù)和方法并進行測試
如果要從默認的查找規(guī)則中忽略查找路徑,可以加上 --ingore 參數(shù),例如:
pytest--ignore=test_case/xxx.py
3.2 執(zhí)行選擇用例
1、執(zhí)行單個模塊中的全部用例:
py.testtest_demo.py
2、執(zhí)行指定路徑下的全部用例:
py.testsomepath
3、執(zhí)行字符串表達式中的用例:
py.test-kstringexpr
4、運行指定模塊中的某個用例,如運行 test_demo.py 模塊中的 test_func 測試函數(shù):
pytesttest_demo.py::test_func
5、運行某個類下的某個用例,如運行 TestClass 類下的 test_method 測試方法:
pytesttest_demo.py::test_method
4.Pytest Fxiture特性
fixture是pytest特有的功能,它用pytest.fixture標識,定義在函數(shù)前面。在編寫測試函數(shù)的時候,可以將此函數(shù)名稱做為傳入?yún)?shù),pytest 將會以依賴注入方式,將該函數(shù)的返回值作為測試函數(shù)的傳入?yún)?shù)。
pytest.fixture(scope='function',params=None,autouse=False,ids=None)
4.1 作為參數(shù)
fixture 可以作為其他測試函數(shù)的參數(shù)被使用,前提是其必須返回一個值:
@pytest.fixture() defhello(): return"hello" deftest_string(hello): asserthello=="hello","fixtureshouldreturnhello"
4.2 作為 setup
fixture也可以不返回值,這樣可以用于在測試方法運行前運行一段代碼:
@pytest.fixture()#默認參數(shù),每個測試方法前調(diào)用 defbefore(): print('beforeeachtest') deftest_1(before): print('test_1()') @pytest.mark.usefixtures("before") deftest_2(): print('test_2()')
這種方式與setup_method、setup_module等的用法相同,其實它們也是特殊的 fixture。
在上例中,有一個測試用了pytest.mark.usefixtures裝飾器來標記使用哪個 fixture,這中用法表示在開始測試前應用該 fixture 函數(shù)但不需要其返回值。
4.3 fixture作用范圍
fixtrue 可以通過設置 scope 參數(shù)來控制其作用域(同時也控制了調(diào)用的頻率)。如果 scope='module',那么 fixture 就是模塊級的,這個 fixture 函數(shù)只會在每次相同模塊加載的時候執(zhí)行。這樣就可以復用一些需要時間進行創(chuàng)建的對象。fixture 提供四種作用域,用于指定 fixture 初始化的規(guī)則:
function:每個測試函數(shù)之前執(zhí)行一次,默認
class: 每個類之前執(zhí)行一次,
module:每個模塊加載之前執(zhí)行一次
session:每次 session 之前執(zhí)行一次,即每次測試執(zhí)行一次
4.4 反向請求
fixture 函數(shù)可以通過接受 request 對象來反向獲取請求中的測試函數(shù)、類或模塊上下文。例如:
@pytest.fixture(scope="module") defsmtp(request): importsmtplib server=getattr(request.module,"smtpserver","smtp.qq.com") smtp=smtplib.SMTP(server,587,timeout=5) yieldsmtp smtp.close()
有時需要全面測試多種不同條件下的一個對象,功能是否符合預期。可以通過設置 fixture 的 params 參數(shù),然后通過 request 獲取設置的值:
classFoo(object): def__init__(self,a,b,c): self.a=a self.b=b self.c=c defecho(self): print(self.a,self.b,self.c) returnTrue @pytest.fixture(params=[["1","2","3"],["x","y","z"]]) deffoo(request): returnFoo(*request.param) deftest_foo(foo): assertfoo.echo()
設置 params 參數(shù)后,運行 test 時將生成不同的測試 id,可以通過 ids 自定義 id:
@pytest.fixture(params=[1,2,4,8],ids=["a","b","c","d"]) defparam_a(request): returnrequest.param deftest_param_a(param_a): printparam_a
4.5 setup/teardown
setup/teardown是指在模塊、函數(shù)、類開始運行以及結束運行時執(zhí)行一些動作。比如在一個函數(shù)中測試一個數(shù)據(jù)庫應用,測需要在函數(shù)開始前連接數(shù)據(jù)庫,在函數(shù)運行結束后斷開與數(shù)據(jù)庫的連接。setup/teardown是特殊的 fixture,其可以有一下幾種實現(xiàn)方式:
#模塊級別 defsetup_module(module): pass defteardown_module(module): pass #類級別 @classmethod defsetup_class(cls): pass @classmethod defteardown_class(cls): pass #方法級別 defsetup_method(self,method): pass defteardown_method(self,method): pass #函數(shù)級別 defsetup_function(function): pass defteardown_function(function): pass
有時候,還希望有全局的 setup 或 teardown,以便在測試開始時做一些準備工作,或者在測試結束之后做一些清理工作。這可以用 hook 來實現(xiàn):
defpytest_sessionstart(session): #setup_stuff defpytest_sessionfinish(session,exitstatus): #teardown_stuff
也可以用 fixture 的方式實現(xiàn):
@fixture(scope='session',autouse=True) defmy_fixture(): #setup_stuff yield #teardown_stuff
4.6 自動執(zhí)行
有時候需要某些 fixture 在全局自動執(zhí)行,如某些全局變量的初始化工作,亦或一些全局化的清理或者初始化函數(shù)。這時可以通過設置 fixture 的 autouse 參數(shù)來讓 fixture 自動執(zhí)行。設置為 autouse=True 即可使得函數(shù)默認執(zhí)行。以下例子會在開始測試前清理可能殘留的文件,接著將程序目錄設置為該目錄:
work_dir="/c/temp" @pytest.fixture(scope="session",autouse=True) defclean_workdir(): shutil.rmtree(work_dir)
5. Pytest Mark特性
Pytest中marker 的作用是,用來標記測試,以便于選擇性的執(zhí)行測試用例。Pytest 提供了一些內(nèi)建的 marker:
#跳過測試 @pytest.mark.skip(reason=None) #滿足某個條件時跳過該測試 @pytest.mark.skipif(condition) #預期該測試是失敗的 @pytest.mark.xfail(condition,reason=None,run=True,raises=None,strict=False) #參數(shù)化測試函數(shù)。給測試用例添加參數(shù),供運行時填充到測試中 #如果parametrize的參數(shù)名稱與fixture名沖突,則會覆蓋掉fixture @pytest.mark.parametrize(argnames,argvalues) #對給定測試執(zhí)行給定的fixtures #這種用法與直接用fixture效果相同 #只不過不需要把fixture名稱作為參數(shù)放在方法聲明當中 @pytest.mark.usefixtures(fixturename1,fixturename2,...) #讓測試盡早地被執(zhí)行 @pytest.mark.tryfirst #讓測試盡量晚執(zhí)行 @pytest.mark.trylast
其中使用 pytest.skip 和 pytest.xfail 能夠實現(xiàn)跳過測試的功能,skip 表示直接跳過測試,而 xfail 則表示存在預期的失敗。
除了內(nèi)建的 markers 外,pytest 還支持沒有實現(xiàn)定義的 markers,如:
@pytest.mark.old_test deftest_one(): assertFalse @pytest.mark.new_test deftest_two(): assertFalse @pytest.mark.windows_only deftest_three(): assertFalse
通過使用 -m 參數(shù)可以讓 pytest 選擇性的執(zhí)行部分測試:
$pytesttest.py-m'notwindows_only'
更詳細的關于 marker 的說明可以參考官方文檔:https://docs.pytest.org/en/latest/example/markers.html
6. conftest.py文件
從廣義理解,conftest.py是一個本地的 per-directory 插件,在該文件中可以定義目錄特定的hooks和fixtures。py.test 框架會在它測試的項目中尋找conftest.py文件,然后在這個文件中尋找針對整個目錄的測試選項,比如是否檢測并運行 doctest 以及應該使用哪種模式檢測測試文件和函數(shù)。
總結起來,conftest.py 文件大致有如下幾種功能:
Fixtures: 用于給測試用例提供靜態(tài)的測試數(shù)據(jù),其可以被所有的測試用于訪問,除非指定了范圍。
加載插件: 用于導入外部插件或模塊:pytest_plugins ="myapp.testsupport.myplugin"
定義鉤子: 用于配置鉤子(hook),如pytest_runtest_setup、pytest_runtest_teardown、pytest_config等。
測試根路徑: 如果將 conftest.py 文件放在項目根路徑中,則 pytest 會自己搜索項目根目錄下的子模塊,并加入到 sys.path 中,這樣便可以對項目中的所有模塊進行測試,而不用設置 PYTHONPATH 來指定項目模塊的位置。
可以有多個conftest.py文件同時存在,其作用范圍是目錄。例如測試非常復雜時,可以為特定的一組測試創(chuàng)建子目錄,并在該目錄中創(chuàng)建conftest.py文件,并定義一個 futures 或 hooks。就像如下的結構:
test_case ├──conftest.py ├──module1 │└──conftest.py ├──module2 │└──conftest.py └──module3 └──conftest.py
7. Pytest插件機制
Pytest之所以稱之為全功能測試框架,得益于它能通過外部插件或者自定義插件的形式擴展所需的功能,這里推薦幾款常用的第三方插件:
pytest-xdist: 分布式測試
pytest-cov: 生成測試覆蓋率報告
pytest-pep8: 檢測代碼是否符合 PEP8 規(guī)范
pytest-flakes: 檢測代碼風格
pytest-html: 生成 html 報告
pytest-randomly: 測試順序隨機
pytest-rerunfailures: 失敗重試
pytest-timeout: 超時測試
審核編輯:黃飛
-
自動化測試
+關注
關注
0文章
208瀏覽量
26905 -
函數(shù)
+關注
關注
3文章
4327瀏覽量
62569 -
python
+關注
關注
56文章
4792瀏覽量
84627
原文標題:聊一聊,Python自動化測試框架
文章出處:【微信號:TestinChina,微信公眾號:Testin云測】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論