介紹
- pytest是基於unittest開發的另一款更高階更好用的單元測試框架
- 支援引數化
- 執行測試過程中可以將某些測試跳過(skip),或者對某些預期失敗的case標記成失敗
- 支援執行由 nose, unittest 編寫的測試 case
- 方便的和持續整合工具 jenkins 整合
- 具有很多第三方外掛,並且可以自定義擴充套件
- 可支援執行部分用例
- 支援失敗重跑功能
安裝
- pytest是第三方庫,需要安裝後使用
- 在命令列輸入以下任意命令即可安裝,使用第三方映象源可加快下載速度
# 豆瓣源
pip install pytest -U -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
# 清華源
pip install pytest -U -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn
命名規範
- 用例檔案應當以 test_*.py 進行命名,或者以 *_test.py命名
- 類必須以 Test 開頭,且類當中不能有__init__方法
- 方法或函式必須以 test_ 開頭
- 可以在專案根目錄建pytest.ini檔案,在裡面自定義用例匹配規則
# 用例匹配規則
[pytest]
python_files = test_*.py *_test.py
python_classes = Test*
python_functions = test_*
斷言
- 斷言必須使用assert,語法:assert 表示式
- 測試的結果, .表示成功 F表示失敗,如果測試失敗,會顯示具體的語句
assert 1==2 #判斷等式兩邊是否相等
assert 200 #判斷某個語句是否為真
assert 10 in [10,20] #判斷某個值是否屬於某個物件
assert not True #判斷某個語句是否不為真
assert 1!=2 #判斷某個值是否不等於另一個值
初始化和清除
def setup_module():
print('\n *** 模組初始化開始 ***')
pass # 這裡寫初始化程式碼
print('\n *** 模組初始化結束 ***')
def teardown_module():
print('\n *** 模組清除開始 ***')
pass # 這裡寫清除程式碼
print('\n *** 模組清除結束 ***')
class Test_A:
@classmethod
def setup_class(cls):
print(f'\n === {cls.__name__}類初始化 ===')
@classmethod
def teardown_class(cls):
print(f'\n === {cls.__name__}類清除 ===')
def setup_method(self):
print('\n --- 類方法初始化 ---')
def teardown_method(self):
print('\n --- 類方法清除 ---')
# 在類外面自定義一個方法,名字隨意,但是要帶上pytest的裝飾器
@pytest.fixture(scope='function')
def custom_setup():
print(f'\n --- 自定義方法初始化 ---')
pass # 在這寫要初始化的程式碼
yield custom_setup # 初始化函式名(不要帶括號)
custom_teardown() # 清除函式
# 同時要自定義一個清除函式,名字隨意
def custom_teardown():
print(f'\n --- 自定義方法清除 ---')
pass # 在這寫清除的程式碼
# 如果有個方法需要呼叫自定義的初始化和清除函式,只需要把初始化函式傳入即可
def test_abc(custom_setup):
pass
- 執行順序(以類中的一個方法為例):模組初始化→類初始化→類方法初始化→自定義方法初始化→自定義方法清除→類方法清除→類清除→模組清除
- 類方法的初始化和清除會作用於類裡的每一個函式
- 自定義方法的初始化和清除只會對傳入該方法名的函式生效
引數化
- 使用@pytest.mark.parametrize(引數名, 引數值列表)
# 傳入多個引數時,引數值列表中,每一組資料的型別都是元組
@pytest.mark.parametrize("val_a,val_b", [(1, 2), (3, 4), (5, 6)])
def test_parametrize_1(val_a,val_b):
print(f"變數val_a的值:{val_a},變數val_b的值:{val_b}")
# 可以使用多個引數化裝飾器,測試資料為不同資料的笛卡爾積,實際會生成m*n條用例(下面的程式碼生成3*4=12條用例)
@pytest.mark.parametrize('a', [1,2,3])
@pytest.mark.parametrize('b', [4,5,6,7])
def test_parametrize_2(a, b):
print(f'笛卡爾積 測試資料為:{a},{b}')
mark標記用例
- pytest支援案例標記的功能,可以給每個用例加上自定義標記,在執行測試案例時通過標記篩選要執行的案例
- 使用標記方法
- 在 pytst.ini 中新增標記名稱
- 在需要標記的函式上使用裝飾器 @pytest.mark.標記名
- pytst.ini :pytest的配置檔案
# 在pytst.ini檔案的標記名列表中新增自定義的標記
[pytest]
markers =
smoke
test
hello
# 在需要標記的函式上增加裝飾器(以smoke,hello為例)
@pytest.mark.smoke
def test_001(self):
print('我是一個冒煙測試用例')
@pytest.mark.hello
def test_002(self):
print('我是一個屬於hello標記的測試用例')
執行用例
- '-k 標記名' ==>可選擇執行該標記所包含的用例
- '-s' ==>在控制檯輸出執行過程中的print內容
- '-q' ==>列印用例執行的簡略過程
- '-v' ==>列印用例執行的詳細過程
- '-x' ==>用例執行失敗則立即停止執行
- '--maxfail=num' ==>用例執行時允許的最大失敗次數,超過num次則立即停止
- '-l' ==>用例執行失敗時,列印相關的區域性變數
- 不輸入引數預設執行該當前目錄及子目錄中的所有測試用例
- pytest執行案例時使用pytest.main()函式,引數傳入一個列表
# 執行所有標記為smoke的用例,顯示詳細過程,並列印print輸出
pytest.main(['-k','smoke','-sv'])
- 如果執行過程中出現了Module already imported so cannot be rewritten的警告,那麼可以用子程序的方式來執行用例,就不會彈出這種警告了
# 匯入子程序模組,避免執行時彈出警告
import subprocess
# 列表中第一個元素為pytest,後面的元素可以輸入各種引數
subprocess.call(['pytest', '-k smoke', '-s'])
# 執行指定目錄下用例
subprocess.call(['pytest', './test'])
# 執行指定模組
subprocess.call(['pytest', './test/test_demo.py'])
# 執行模組中的指定用例
subprocess.call(['pytest', './test/test_demo.py::test_001'])
# 執行類中的指定用例
subprocess.call(['pytest', './test/test_demo.py::TestDemo::test_001'])
# 執行test_demo.py模組下名稱包含hello的用例
subprocess.call(['pytest', '-k hello','./test/test_demo.py'])
# 執行TestDemo類中名稱包含aa的用例
subprocess.call(['pytest', '-k aa','./test/test_demo.py::TestDemo'])
allure安裝
allure裝飾器
- 根據實際需要對用例使用allure裝飾器,可以提升allure報告的可讀性
- 幾種常用的裝飾器:
@allure.epic('層級1')
@allure.feature('層級2')
@allure.story('層級3')
@allure.title('用例標題')
@allure.description('用例描述')
allure報告
- allure生成報告分為兩步:
- 第一步先執行用例並生成測試資料(一大堆json檔案)
- 根據某次執行用例後生成的測試資料,通過allure generate命令來生成測試報告(包含了index.html,可在瀏覽器開啟檢視)
- 生成allure測試資料
# 這裡我是在每次執行的時候,將當天的測試資料和測試報告按日期+時間的雙層目錄來存放
date = time.strftime('%Y%m%d', time.localtime()) # 當前日期
time = time.strftime('%H%M%S', time.localtime()) # 當前時間
# allure命令要在命令列使用,在python中可通過os.system來呼叫
os.system(f'pytest --alluredir ./result/{date}/{time}/') # 生成測試結果
# 根據測試結果生成測試報告
# 加上--clean引數可以先清除資料,但由於我是按時間存放的,所以每次生成的目錄都在不同位置,這裡就不clean了,自己除錯程式碼測試的時候可以用上clean
os.system(f'allure generate ./result/{date}/{time}/ -o ./report/{date}/{time}/')
- 手動開啟測試報告:在生成的測試報告資料夾裡有index.html檔案,直接在瀏覽器開啟即可檢視
- 也可以起一個服務,會自動開啟allure測試報告
- 注意:使用這個命令後會在本地起一個服務,如果開啟的網頁是空白的(可能存在網路限制等各種原因),那麼可以將ip換成 127.0.0.1:(啟動服務後命令行顯示的埠號) 試下能不能正常訪問
# 自動開啟測試報告,將啟動後的服務ip換成 127.0.0.1
os.system(f'allure serve ./result/{date}/{time}/')