介紹

  • 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支援案例標記的功能,可以給每個用例加上自定義標記,在執行測試案例時通過標記篩選要執行的案例
  • 使用標記方法
    1. 在 pytst.ini 中新增標記名稱
    2. 在需要標記的函式上使用裝飾器 @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:https://github.com/allure-framework/allure2/releases
  • 解壓allure.zip到一個檔案目錄中
  • 將allure報告安裝目錄\bin所在的路徑新增環境變數path中
  • 命令列輸入pip install allure-pytest -i http://pypi.douban.com/simple/
  • 在命令列中輸入allure,如果能看到命令就是已經配置完成

allure裝飾器

  • 根據實際需要對用例使用allure裝飾器,可以提升allure報告的可讀性
  • 幾種常用的裝飾器:
@allure.epic('層級1')
@allure.feature('層級2')
@allure.story('層級3')
@allure.title('用例標題')
@allure.description('用例描述')

allure報告

  • allure生成報告分為兩步:

    1. 第一步先執行用例並生成測試資料(一大堆json檔案)
    2. 根據某次執行用例後生成的測試資料,通過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}/') # 生成測試結果
  • 生成allure測試報告
# 根據測試結果生成測試報告
# 加上--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}/')