參考學習官網文檔:https://docs.pytest.org/en/latest/getting-started.html
一、pytest簡介
1. 前言:測試框架與測試工具
- 測試框架、測試工具這兩個概念經常容易搞混了,測試框架是諸如 Unittest、Pytest、TestNG 這類,而測試工具指的則是 Selenium、Appium、Jmeter 這類。
- 測試框架的作用是,幫助管理測試用例、執(zhí)行測試用例、參數(shù)化、斷言、生成測試報告等基礎性工作,讓我們將精力用在測試用例的編寫上。好的測試框架應該具有很高的擴展性,支持二次開發(fā),并能夠支持多種類型的自動化測試。
- 測試工具的作用是為了完成某一類型的測試,比如 Selenium 用于對 WEB UI 進行自動化測試,Appium 用來對 APP 進行自動化測試,Jmeter 可以用來進行 API 自動化測試和性能測試。另外,Java 語言中 OkHttp 庫,Python 語言中的 requests 庫,這些 HTTP 的 client 也可以看做是一種 API 測試工具。
2. 單元測試框架
- 1)什么是單元測試框架?
單元測試是指軟件開發(fā)中,針對軟件的最小單位(函數(shù)、方法)進行正確性的檢查測試,Java:junit、testing
Python:unittest、pytest- 2)單元測試框架主要做什么?
① 測試發(fā)現(xiàn):從多個文件里面去找我們得測試用例
② 測試執(zhí)行:按照一定的順序和規(guī)則去執(zhí)行,并生成結果
③ 測試判斷:通過斷言判斷預期結果和實際結果的差異
④ 測試報告:統(tǒng)計測試進度、耗時、通過率、生成測試報告
3. pytest簡介
- Pytest 是 Python 的一種易用、高效和靈活的單元測試框架,可以支持單元測試和功能測試
- pytest可以和selenium,requests,appium結合實現(xiàn)web自動化、接口自動化、app自動化
- pytest可以實現(xiàn)測試用例的跳過以及reruns(失敗用例重試)
- pytest可以和allure生成非常美觀的測試報告
- pytest可以和jenkins持續(xù)集成
- pytest有很多非常強大的插件,并且這些插件能夠實現(xiàn)很多實用的操作
① pytest-xdist:測試用例分布式執(zhí)行,多cpu分發(fā)
② pytest-ordering:用于改變測試用例的執(zhí)行順序
③ pytest-rerunfailures:用例失敗后重跑
④ pytest-html:生成html格式的自動化測試報告
⑤ allure-pytest:用于生成美觀測試報告
二、安裝pytest
pytest需要:Python 3.7+ 或 PyPy3
- 在命令行中運行以下命令
pip install -U pytest
其他插件安裝方法也是一樣pip安裝
-
檢查是否安裝成功pytest
三、pytest使用規(guī)則
- 模塊名必須以
test_開頭或test_結尾 - 測試類必須以
Test開頭,且不能有init方法 - 測試方法必須以
test開頭
使用規(guī)則
四、測試用例執(zhí)行
pytest 用例的執(zhí)行主要有以下三種方式:
- 通過命令模式執(zhí)行
- 通過主函數(shù)執(zhí)行
- 通過讀取 pytest.ini 配置文件執(zhí)行
4.1 通過命令模式執(zhí)行
測試文件test_demo.py:Pytest 使用 Python 的 assert 進行條件判斷,簡單的測試函數(shù)如:
def test_product_case001():
print("模擬測試模塊1的功能")
assert 1 + 1 == 3
def test_product_case002():
print("模擬測試模塊2的功能")
assert 1 == 1
1)執(zhí)行所有用例
pytest 命令執(zhí)行當前目錄下所有的測試用例,所以要想執(zhí)行項目中所有的用例,只需要將執(zhí)行目錄切換到根目錄即可:

可以加上-v參數(shù)查看詳情pytest -v

2)指定執(zhí)行
執(zhí)行指定目錄:


4.2 通過主函數(shù)執(zhí)行
1)主函數(shù)運行
test_demo.py添加主函數(shù):
import pytest
class Testdemo:
def test_case_passed(self):
print("模擬執(zhí)行成功場景的case")
assert (1, 2, 3) == (1, 2, 3)
def test_case_failed():
print("模擬執(zhí)行失敗場景的case")
assert 3 == 1 + 1
if __name__ == '__main__':
pytest.main(["-v"]) # 執(zhí)行測試用例,-v參數(shù)打印詳細信息
然后直接運行:
從終端的打印可以看到,用例文件中的用例方法 test_case_failed 以及測試類中的測試方法 test_case_passed 都被執(zhí)行了。
2)批量執(zhí)行
只需要在對應的用例文件中調用主函數(shù),就可以自動執(zhí)行文件中的所有用例。
但是在實際的使用中,一般會將用例分類寫在不同目錄下的不同用例文件里,且執(zhí)行時要批量去執(zhí)行所有的用例,所以往往會將主函數(shù)寫在一個與用例文件夾平級的 py 文件中,pytest 內部設有對應的機制,會自動掃描全局,執(zhí)行所有的用例文件。

運行主函數(shù)文件main.py,輸出結果可以看到所有用例均被執(zhí)行了:

3)指定執(zhí)行
pytest.main()可以加上命令參數(shù)運行,如下



4.3 通過 pytest.ini 配置文件執(zhí)行
在項目中,通常通過配置全局的配置文件來執(zhí)行測試用例的。其余兩種執(zhí)行方式在編寫和調試用例時比較方便。對于配置文件,有以下幾點要求:
- 配置文件的名字必須為 pytest.ini
- 配置文件一般放置在項目的根目錄
- 配置文件必須時 ANSI 編碼
- 配置了pytest.ini文件后,不管是主函數(shù)模式運行用例,還是命令行模式運行用例,都會去讀取pytest.ini配置文件中的內容。
- CMD命令行中執(zhí)行pytest -h命令,可以查看pytest.ini的設置選項。
pytest.ini 是 pytest 框架的核心配置文件,它可以改變 pytest 的默認行為,不管是通過主函數(shù)還是命令模式去執(zhí)行用例,都會先去讀取配置文件。

五、執(zhí)行參數(shù)
我們在執(zhí)行用例時,可以通過添加相關的參數(shù)實現(xiàn)不同的執(zhí)行效果,提升測試效率。
1)-s:用于輸出調試信息,包括打印輸出的信息
# 通過main函數(shù)執(zhí)行
pytest.main(["-s", "./product_cases/test_product.py"])
# 通過命令模式執(zhí)行
pytest -s ./product_cases/test_product.py

2)-v:顯示更詳細的信息
# 通過main函數(shù)執(zhí)行
pytest.main(["-v", "./product_cases/test_product.py"])
# 通過命令模式執(zhí)行
pytest -v ./product_cases/test_product.py

3)-vs:將 -v 和 -s 參數(shù)效果結合,推薦使用
-vs

4)-n:支持多線程或者分布式運行測試用例
n是進程數(shù),也就是cpu個數(shù),最大值為當前機器cpu個數(shù),也可以設置為auto,自動識別cpu個數(shù)。
# 通過main函數(shù)執(zhí)行
# 開兩個線程去執(zhí)行用戶操作目錄下所有的用例
pytest.main(["-vs", "./product_cases", "-n=2"])
# 通過命令模式執(zhí)行
pytest -vs ./product_cases -n 2
為了方便看出測試效果,給test_product.py文件中的每個測試用例都增加sleep(2)
import pytest,time
def test_product_case001():
time.sleep(2)
print("模擬測試模塊1的功能")
assert 1 + 1 == 2
def test_product_case002():
time.sleep(2)
print("模擬測試模塊2的功能")
assert 1 == 1
正常執(zhí)行結果:用時4.05s

5)--reruns:失敗用例重跑
# 通過main函數(shù)執(zhí)行
# 如果用例執(zhí)行失敗,該用例再執(zhí)行2次
pytest.main(["-vs", "./product_cases", "--reruns=2"])
# 通過命令模式執(zhí)行
pytest -vs ./product_cases --reruns 2
修改test_product.py文件
def test_product_case001():
print("模擬測試模塊1的功能")
assert 1 + 1 == 3
def test_product_case002():
print("模擬測試模塊2的功能")
assert 1 == 1

6)-x:只要有一個用例失敗,就停止執(zhí)行
# 通過main函數(shù)執(zhí)行
pytest.main(["-vs", "./product_cases", "-x"])
# 通過命令模式執(zhí)行
pytest -vs ./product_cases -x
test_product_case001失敗,停止執(zhí)行,test_product_case002未執(zhí)行

7)--maxfail:失敗的用例數(shù)達到規(guī)定值時停止測試
# 失敗的用例數(shù)達到2個時停止測試
# 通過main函數(shù)執(zhí)行
pytest.main(["-vs", "./product_cases", "--maxfail=2"])
# 通過命令模式執(zhí)行
pytest -vs ./product_cases --maxfail 2

8)-k:關鍵字過濾執(zhí)行
-k會匹配文件名、類名、方法名匹配表達式的用例,如果指定的表達式是被文件名/測試類名包含的,那么文件/測試類中所有的測試方法都會被執(zhí)行。
# 執(zhí)行文件名/類名/方法名中包含01的用例
# 通過main函數(shù)執(zhí)行
pytest.main(["-vs", "./product_cases", "-k=01"])
# 通過命令模式執(zhí)行
pytest -vs ./product_cases -k="01"
指定執(zhí)行測試用例名稱包含01:

指定執(zhí)行測試用例名稱包含login:

9)--html:用例執(zhí)行完成后生成 html 格式的測試報告
pytest -vs --html ./report/result.html --html+報告存放路徑參數(shù)


六、用例執(zhí)行順序
修改 test_product.py 中測試方法:
def test_product_case():
print("模擬測試產品相關的操作")
def test_case_001():
print("測試方法_001")
def test_case_002():
print("測試方法_002")
def test_case_003():
print("測試方法_003")
def test_case_004():
print("測試方法_004")
def test_case_005():
print("測試方法_005")
1)默認執(zhí)行順序
pytest 對于用例的執(zhí)行順序是按照從上到下執(zhí)行的

2) 改變用例的執(zhí)行順序
通過裝飾器方法@pytest.mark.run()可以改變用例的執(zhí)行順序,參數(shù)order=n指定排在第n個執(zhí)行
# product_cases/test_product.py
import pytest
def test_product_case():
print("模擬測試產品相關的操作")
# 通過裝飾器方法 @pytest.mark.run() 可以改變用例的執(zhí)行順序
# order參數(shù)表示該用例要排在第幾個執(zhí)行
@pytest.mark.run(order=1)
def test_case_002():
print("測試方法_002")
@pytest.mark.run(order=2)
def test_case_003():
print("測試方法_003")
def test_case_004():
print("測試方法_004")
def test_case_005():
print("測試方法_005")

從終端輸出的結果可以看到,被裝飾器裝飾的測試用例都按照了指定的順序執(zhí)行,沒有被裝飾到的用例還是按照從上到下的順序執(zhí)行
七、標記函數(shù),分組執(zhí)行用例
1)裝飾器@pytest.mark.name裝飾用例
第一步,使用裝飾器@pytest.mark.name去裝飾要執(zhí)行的用例,其中 name 為自定義的名稱
# product_cases/test_product.py
import pytest
# 通過裝飾器方法 @pytest.mark.finished標記已完成的用例
@pytest.mark.finished
def test_product_case():
print("模擬測試產品相關的操作")
# 通過裝飾器方法 @pytest.mark.unfinished標記未完成功能的用例
@pytest.mark.unfinished
def test_case_002():
print("測試方法_002")
@pytest.mark.unfinished
def test_case_003():
print("測試方法_003")
def test_case_004():
print("測試方法_004")
def test_case_005():
print("測試方法_005")
2)pytest.ini 添加分組配置
第二步,去 pytest.ini 中添加分組配置,配置名必須和裝飾器中的 name 值一致
3)參數(shù) -m 指定執(zhí)行用例
第三步,執(zhí)行時通過參數(shù) -m 指定要執(zhí)行的用例
八、跳過用例
對于某些不想執(zhí)行的用例,可以使用 pytest 提供的裝飾器方法@pytest.mark.skip()跳過該用例不去執(zhí)行
1)直接跳過
裝飾器語法:@pytest.mark.skip(reason="") ,reason 參數(shù)可選,表示跳過該測試方法的原因

2)條件跳過
裝飾器語法:@pytest.mark.skipif(判斷條件, reason="") ,為測試函數(shù)指定被忽略的條件,reason 參數(shù)可選
# product_cases/test_product.py
import pytest
version = 1
# V2版本才支持,版本小于V2則跳過不執(zhí)行
@pytest.mark.skipif(version < 2, reason='not supported until v2')
def test_product_case():
print("模擬測試產品相關的操作")
def test_case_001():
print("測試方法_001")
def test_case_002():
print("測試方法_002")
-
version=1滿足條件,跳過不執(zhí)行滿足條件跳過
-
version=2不滿足條件,不跳過執(zhí)行不滿足條件不跳過
文章轉載參考:http://www.chendacheng.com/content/?category=Pytest%E4%B8%93%E9%A2%98&article=Pytest_01.html



