一、pytest 介紹、執行、引數化和資料驅動、Fixture
pytest安裝與介紹
- 官網 : pip install -U pytest
- 檢視版本號:pytest --version
為何選擇pytest
- 相容unittest
- 定製化外掛開發
pycharm 配置github
- VSC--Git--Remotes...
pycharm pytest 配置
- settings--搜尋pytest--Python integrated Tools--testing--選擇pytest:根據黃色歎號fix安裝pytest安裝到環境
pytest命名規則
- python風格規範:
https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/ - pytest:
- 檔名: test_開頭 或者 _test 結尾
- 類名:Test 開頭
- 方法(類內)/函式名(類外):test_開頭
- 注意:測試類中不可以新增__init__建構函式
pytest執行測試用例
1 - pycharm
2 - 命令列執行方式 - 推薦(以便後續持續整合)
可能會遇到的找不到模組的路徑問題,需要在導包之前引入路徑 import sys sys.path.append('路徑')
- 執行包: pytest (pytest會自動檢索當前目錄下所有符合規則的測試用例)
- 執行一個模組:pytest -vs test.py
- 只執行某一條測試用例:pytest 目錄/模組.py::類名::用例名
常用命令列引數
- pytest --help: 獲取所有引數列表
- -x: 用例一旦失敗(fail/error),就立刻停止
- 使用場景:一般用於冒煙測試
- 冒煙測試最早由google提出的一個概念,一般針對每日構建的版本,對系統的基本功能進行簡單測試的測試型別,主要強調程式主體的功能
- 使用場景:一般用於冒煙測試
- --maxfail=num: 用例達到最大數num停止執行
- -m: 標記用例
- -k: 執行包含某個關鍵字的測試用例
- -v: 列印詳細日誌
- -s 列印輸出日誌(一般-vs一塊使用)
- --collect-only:(測試平臺,pytest 自動匯入功能)
標記測試用例:mark
- 場景:只執行符合要求的某一部分用例,把專案分為多個模組,然後指定模組名稱執行
- 解決:在測試用例的上方加上pytest裝飾器:@pytest.mark.標籤名
- 執行 -m 標記自定義的相關用例
- pytest -s test_mark.py -m=標籤名
- pytest -s test_mark.py -m 標籤名
- pytest -s test_mark.py -m "not 標籤名" (not:邏輯運算,表示不是標籤名的都去執行)
跳過(skip)及預期失敗(xfail)
pytest的內建標籤,可以處理一些特殊的、及不能成功的測試用例
-
ship: 始終跳過該用例
- 方式1: @pytest.mark.skp(reason="程式碼沒有實現")
- 方式2:在測試用程式碼中新增判斷的語句
def test_demo(): if not login(): pytest.skip("未登入無法執行該用例")
-
skipif: 遇到特定情況跳過該測試用例,需要給定條件
@pytest.mark.skipif(條件="", reason="列印提示資訊內容")
-
xfail: 遇到特殊情況,產生一個“期望失敗”輸出
執行結果分析
- 常用:fail error pass
- 特殊結果:
- warning
- deselect
pytest常用執行引數
----------
更多用法使用 pytest --help 檢視幫助文件
用例匹配:
- pytest -k "add" 匹配所有用例名稱中包含add的用例
- pytest -m mark 標籤名、標記:在測試用例上新增裝飾器 @pytest.mark.login
-
pytest有很多自帶的標籤,我們自己定的標籤,如login 他會不識別,所以在執行後會有 deselected、warnings
警告資訊,需要建立pytest配置檔案:pytest.inipytest.ini [pytest] # markers 幫助文件解釋:為你的測試用例新增標籤 markers = login search '個人中心'
-
- pytest --junitxml=./result.xml 生成執行結果檔案
- pytest --setup-show 回溯fixture的執行過程
pytest.ini:
- pytest.ini是pytest的配置檔案
- 檔案內容要以 [pytest] 開頭
- 修改pytest模組名、類名、用例名稱規則(以什麼開頭:如pytest檔名和用例名預設以test_* 開頭或結尾)
-
python_files (args):用於Python測試模組發現的全域性樣式檔案模式(檔名)
-
python_classes (args):字首或glob名稱,用於發現Python測試類(類名)
-
python_functions (args): Python測試函式和方法發現的字首或glob名稱(方法名)
-
addopts:新增一個或多個命令列引數,如,新增 -vs 命令引數後:執行pytest test_case.py 等同於 pytest -vs test_case.py
pytest.ini
[pytest] # 為測試用例新增標籤 markers = login search # 修改pytest模組名、類名、用例名稱規則(以什麼開頭:如pytest檔名和用例名預設以test_* 開頭或結尾) pytest_files = test_* *_test check_* pytest_functions = test_* *_test check_* # 新增一個或多個命令列引數,如,新增 -vs 命令引數後:執行pytest test_case.py 等同於 pytest -vs test_case.py addopts = -vs --alluredir=./result
-
pytest框架結構
- setup() teardown() 類方法執行前後被呼叫
- 模組級別:setup_module/teardown_module 全域性的、優先順序最高
- 函式級別:setup_function/teardown_function 只在函式用例生效(與方法的級的區別:不在類中)
- 類級別:setup_class/teardown_class 只在類中前後執行一次(在類中)
- 方法級別:setup_method/teardown_method 開始與方法的始末、每個方法執行強後都執行一遍(與函式級別的區別:方法在類中,函式不在類中)
pytest引數化與資料驅動
- 引數化一般與資料驅動一起使用,
- 引數化:將變化的測試資料以引數的形式傳入到測試方法,待測試的輸入和輸出是一組資料,可以把測試資料組織起來,用不同的測試資料呼叫相同的測試方法
- 資料驅動:資料的改變從而驅動自動化的執行,最終引起測試結果的改變,測試資料的資料驅動、測試步驟的資料驅動、配置的資料驅動、po的資料驅動
pytest引數化
- pytest自帶引數化概念
- 加入引數化裝飾器:@pytest.mark.parametrize("a, b, result", [1, 2, 3], [2, 3, 5], [0.3, 1, 1.3])
- ids引數:定義每一組測試資料生成的測試用例的別名,多少組資料就對應有多少個ids
- 加入引數化裝飾器:@pytest.mark.parametrize("a, b, result", [1, 2, 3], [2, 3, 5], [0.3, 1, 1.3])
import pytest
import yaml
def add(a, b):
return a + b
class TestDate:
@pytest.mark.parametrize("a, b", [(10, 20), (10, 30), (2, 6)])
def test_param(self, a, b):
print(a + b)
@pytest.mark.parametrize(["x", "y"], [(10, 20), (10, 30)])
def test_param2(self, x, y):
# print(x + y)
print(add1(x, y))
@pytest.mark.parametrize("a", "b", yaml.safe_load(open(r'E:\data.yaml')))
def test_param5(self, a, b):
print(f'相加等於{add(a, b)}')
- 執行用例test_parm會得出三條測試用例資料:
pytest異常捕獲
設計測試用例的時候要儘可能的區分型別,比如除數為0時,預期結果是丟擲異常,則此用例pass,所以使用pytest捕獲ZeroDivisionError異常,如果捕獲到,則不報出異常 則此測試用例通過
with pytest.raises(ZeroDivisionError, TypeError):
fixture高階用法
fixture 介紹:
- 官網 Features--Modular有詳細介紹
- fixture又名測試樁子,類似setup teardown
- fixture 能做到setup teardown 做不到的事情
-
比如:一個模組裡又很多用例,這些方法有的需要的前置條件不一樣,如有的需要登入,有的不需要登入
def login(): print('登入') def test_search(): print('搜尋') def test_search(): print('購物') def test_search(): print('下單')
-
如,如果使用setup teardown 只能是用例前後被呼叫,但是去搜尋,首先不需要登入,所以就可以把login定義為fixture函式,然後傳入到測試方法裡
使用方法:在被重複呼叫的方法上加上@pytest.fixture()裝飾器,如果某個方法需要登入,就傳入login@pytest.fixture() def login(): print('登入') def test_search(): print('搜尋') def test_search(login): print('購物') def test_search(login): print('下單')
- 在命令列中使用執行回溯,可以看到fixture執行的詳細過程:pytest test_case.py --setup-show
-
fixture 作用
- Fixture是在測試函式執行前後,由pytest執行的外殼函式,程式碼可以定製,滿足多變的測試需求,功能包括:
- 定於傳入測試中的資料集
- 配置測試前系統的初始狀態
- 為批量測試提供資料來源等
- Fixture是pytest用於將測試前後進行預備,清理工作的程式碼分類核心測試邏輯的一種機制
fixture用法
- 1- 類似setup teardown功能,但比setup teardown更靈活
- 2- 直接通過函式名呼叫或者裝飾去@pytest.mark.usefixtures('test1')
- 3- 允許使用多個Fixture
- 4- 使用autouse自動應用,如果由返回值,需要穿fixture函式名
- 5- 作用域 session>module>class>function
使用-- setup-show 回溯fixture的執行過程
- pytest test_case.py --setup-show
pytest yield
-
以上fixture記錄的僅僅是setup,即測試用例執行前的操作,如果需要加上teardown的操作,需要在fixture函式中加入 yield
@pytest.fixture() def login(): print('登入') # 用例執行前登入 yield # 相當與 return 可以返回一些資料 如 yield token print('退出') # 用例結束後退出
conftest + fixture
-
conftest.py檔案,它主要是實現資料(fixture)共享的檔案,名字是固定的。
- 1- 第一,conftest.py檔案當中,它儲存的都是fixture,就是給用例提供做前置準備工作和後置清理工作的一個東西;
- 2- 第二,conftest.py檔案可以將它的fixtures共享到它自己目錄下的所有用例,用例當中如果使用fixture的話,是不需用匯入conftest.py這個檔案的,會直接自動去查詢;
- 3- 第三,conftest.py它是屬於層級共享的,也就是說,一個自動化專案當中,可以在不同的包下面去建立conftest.py這個檔案。
-
執行原則:就近原則,如果當前包下沒有conftest.py就會往上一層找
fixture 引數化
二、常用外掛、資料驅動、編寫pytest外掛、Allure
pytest常用外掛
pip install pytest-ordering:控制用例的執行順序
- 建議在編寫測試用例的時候,能不讓用例順序執行就不順序執行,這樣可能會更好的暴露出問題
- 在測試方法上加上裝飾器:@pytest.mark.run(order=2)
pip install pytest-dependency:控制用例的依賴關係
ps:設計測試用例的時候進可能不要讓用例有順序,不要讓測試用例有依賴關係,如果無法做到,可以臨時的用外掛解決
-
官網 : https://pytest-dependency.readthedocs.io/en/latest/usage.html#basic-usage
-
設定一個用例和一個用例的依賴關係:一旦用例A失敗,那麼用例B也不會執行。比如:用例A為新增購物車,用例B為去結算,那麼如果用例A沒有登入就會執行失敗,則用例B也就會執行失敗
pytest-dependency 要做的就是,讓他們之間存在一種依賴關係:一旦用例A失敗,那麼用例B也不會執行 SKIPimport pytest # 官方demo: @pytest.mark.dependency() # test_a測試用例設定了dependency依賴關係 @pytest.mark.xfail(reason="deliberate fail") def test_a(): assert False @pytest.mark.dependency() # test_b測試用例設定了dependency依賴關係 def test_b(): pass @pytest.mark.dependency(depends=["test_a"]) # test_c 依賴於測試用例 test_a。 因為test_a斷言失敗,所以test_c會跳過 def test_c(): pass @pytest.mark.dependency(depends=["test_a"]) # test_d 依賴於測試用 test_b def test_d(): pass @pytest.mark.dependency(depends=["test_b", "test_c"]) test_e依賴與測試用例 test_b 和 test_c 因為test_b通過了,但test_c跳過,所以test_e也會跳過 def test_e(): pass
pip install pytest-xdist:分散式併發執行測試用例
- 機制:哪個cpu先執行完一個任務,就下發另一個任務
- pytest -n 3 :3=當前系統的cpu個數,如果不知系統有多少個cpu或者不知道可以承載多少個程式,使用 pytest -n aotu 自動分發空閒的程式
pip install pytest-rerunfailures:失敗重跑
- pytest --reruns 5 : 如果用例失敗,就會重新跑5次
- pytest --reruns 5 --reruns-delay 2:用例失敗後等待2秒後再執行,或者再用例方法上加上裝飾器@pytest.mark.flaky(5, reruns-delay=2)
pip install pytest-assume:多重校驗
- 通常一個測試方法中會寫多個斷言,如果第一條斷言過不去,下面的就不執行了,如果我們想報錯也往下執行,使用pytest提供的斷言方法方法
- pytest.assume(1==4) 相當於 assert 1==4 # 此方法應用場景較少
pip install pytest-random-order:用例隨機執行
- pytest --random-order-seed=
設定一個種子,使用比較少
pip install pytest-html:測試報告
- 後續使用allure,不做介紹
三、pytest高階用法 hook函式
pytest外掛載入方式
-
內建plugin
- 從程式碼內部的 _pytest 目錄載入
- External Libraries - site-package - _pytest - hookspec.py
- hookspec.py 有很多方法 即我們所說的hook方法,我們可以改寫hook方法來滿足我們一些需求
- External Libraries - site-package - _pytest - hookspec.py
- 從程式碼內部的 _pytest 目錄載入
-
外部外掛(第三方外掛)
- 通過setuptools entry points 機制發現的第三方外掛(https://docs.pytest.org/en/latest/plugins.html)
-
conftest.py 存放的本地外掛(重點):
- 自動模組發現機制
-
pytest --trace-config 檢視當前pytest中所有的plugin(帶有hook方法的檔案)
Allure 生成測試報告
另起...