一、Pytest配置框架
Pytest的配置旨在改變其默認行為,以適應不同的測試需求和項目結構。理解其配置層級和常用參數,是高效使用Pytest的基礎。
1. 配置的意義與層級
配置的本質在于提供一種機制,允許用戶根據項目特點、團隊規范或特定測試場景,調整Pytest的運行方式。Pytest的配置具有多層級性,優先級從低到高依次為:
?默認配置:Pytest內置的默認行為。
?環境變量:以PYTEST_開頭的大寫環境變量,例如PYTEST_ADDOPTS。
?配置文件:pytest.ini、pyproject.toml或setup.cfg等配置文件。
?命令行參數:通過命令行傳遞的參數,優先級最高。
這種層級結構確保了配置的靈活性和可覆蓋性,允許用戶在不同粒度上進行定制。
2. 獲取所有配置信息
要全面了解Pytest支持的所有配置選項,可以通過運行pytest -h命令來獲取幫助信息。該命令會列出所有可用的命令行參數、INI配置選項以及相關的環境變量。
命令:
pytest -h
結果為:
- 輸出解析:
?-開頭的選項:通常是命令行參數,用于在執行時直接影響Pytest行為。
?小寫字母開頭的選項:通常是可以在pytest.ini等配置文件中設置的INI配置項。
?大寫字母開頭的選項:通常是環境變量,可以在系統層面或腳本中設置。
3. 常用的命令行參數
命令行參數是臨時調整Pytest行為最直接的方式,它們在當前測試運行中具有最高優先級。
參數 | 描述 | 示例用法 | 備注 |
-v | 增加輸出的詳細程度,顯示每個測試用例的名稱和結果。 | pytest -v | 適用于需要查看每個測試執行狀態的場景。 |
-s | 停止捕獲標準輸出(stdout)和標準錯誤(stderr),允許測試用例中的print()函數直接輸出到控制臺。 | pytest -s | 在調試測試用例時非常有用,可以直接看到print的輸出。 |
-x | 遇到第一個失敗的測試用例時立即停止測試執行。 | pytest -x | 適用于快速定位問題,避免不必要的后續測試。 |
-m <MARKEXPR> | 根據標記表達式篩選要執行的測試用例。 | `pytest -m |
api|<MARKEXPR>可以是標記名(如api),也可以是邏輯表達式(如'api and web'或'not slow')。 | | -k <EXPR>| 根據表達式篩選測試用例的名稱。 |pytest -k "test_add and not test_list"| 匹配測試函數、方法或類的名稱。 | |--collect-only| 只收集測試用例,不執行。 |pytest --collect-only| 適用于檢查測試發現是否符合預期。 | |--lf/--last-failed| 只運行上次失敗的測試用例。 |pytest --lf| 快速重跑失敗用例,提高調試效率。 | |--ff/--failed-first| 先運行上次失敗的測試用例,然后運行其他測試。 |pytest --ff` | 優先驗證修復效果,同時不遺漏其他測試。 |
示例代碼(結合-s參數):
Python
import pytestdef add(a, b):return a + b# 示例:測試失敗和print輸出
def test_print_and_fail():print("Hello from test_print_and_fail!")assert 1 == 2 # 預期失敗# 示例:測試輸入
def test_input_example():# 當使用 `pytest -s` 時,input() 可以正常工作user_input = input("請輸入您的姓名:")print(f"您輸入的是:{user_input}")assert len(user_input) > 0class TestCalculator:def test_int_addition(self):assert add(1, 2) == 3def test_string_concatenation(self):assert add("hello", "world") == "helloworld"def test_list_concatenation(self):# 列表的加法是拼接操作assert add([1], [2, 3, 4]) == [1, 2, 3, 4]
4. pytest.ini配置文件
pytest.ini是Pytest最常用的配置文件,它允許我們持久化配置選項,例如注冊自定義標記、設置默認命令行參數、配置測試發現規則等。它通常放置在項目的根目錄下。
pytest.ini 示例結構:
[pytest]
# Pytest的最低版本要求
minversion = 6.0# 默認添加到命令行參數的選項
# -s: 允許捕獲print輸出
# -v: 增加輸出詳細程度
# --strict-markers: 強制要求所有使用的標記都必須在markers中注冊
addopts = -s -v --strict-markers# 注冊自定義標記及其描述
markers =api: 標記接口測試用例web: 標記UI(Web)測試用例ut: 標記單元測試用例login: 標記登錄相關測試用例pay: 標記支付相關測試用例ddt: 標記數據驅動測試用例smoke: 標記冒煙測試用例,用于快速驗證核心功能# 配置測試發現規則 (可選)
python_files = test_*.py *_test.py
python_classes = Test*
python_functions = test_# 配置測試路徑 (可選)
# testpaths = tests/# 配置報告輸出 (可選)
# log_cli = true
# log_cli_level = INFO
# log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
說明:
?[pytest]:配置文件的主體部分。
?minversion:指定Pytest的最低版本要求,如果當前Pytest版本低于此值,則會發出警告。
?addopts:用于設置每次運行Pytest時默認添加的命令行選項。這對于團隊協作和CI/CD環境非常有用,可以確保測試運行的一致性。
?markers:這是非常重要的部分,用于注冊自定義標記。在pytest.ini中注冊標記可以避免PytestUnknownMarkWarning警告,并為標記提供清晰的描述,方便團隊成員理解和使用。
二、標記mark
標記 可以讓用例與眾不同,進而可以讓用例被區別對待
(一)、用戶自動義標記
標記是Pytest中一種強大的元數據機制,它允許我們為測試函數、測試方法或測試類附加額外的信息。通過標記,我們可以對測試用例進行分類、篩選,甚至改變它們的執行行為。
1. 用戶自定義標記
用戶自定義標記主要用于對測試用例進行分類和篩選。它們需要在使用前在pytest.ini文件中進行注冊。
步驟:
1、先注冊
[pytest]markers =api:接口測試web:UI測試ut:單元測試login:登錄相關pay:支付相關
2、再標記
實例:
import pytestdef add(a, b):return a + bclass TestAdd:@pytest.mark.apidef test_int(self):assert add(1, 2) == 3@pytest.mark.webdef test_str(self):assert add("1", "2") == "12"@pytest.mark.paydef test_list(self):assert add([1] ,[2,3,4,5]) == [1,2,3,4,5]
3、后篩選
利用命令: pytest -m 參數
實例:
pytest -m web
結果:
(二)、框架內置標記
Pytest提供了一些內置標記,它們無需在pytest.ini中注冊即可直接使用,并且除了篩選功能外,還具有特殊的執行效果。這些標記通常用于控制測試的跳過、預期失敗以及參數化等高級行為。
和用戶自定義的區別
特性 | 用戶自定義標記 | 框架內置標記 |
注冊 | 必須在pytest.ini中注冊 | 無需注冊,直接使用 |
功能 | 主要用于篩選測試用例 | 除了篩選,還具有特殊的執行效果(如跳過、預期失敗) |
效果 | 僅影響測試的執行范圍 | 影響測試的執行結果和報告狀態 |
1、無需注冊、可以直接使用
2、不僅可以篩選,還可以增加特殊效果
3、不同的標記、增加不同的特殊效果
- skip:無條件跳過
@pytest.mark.skip裝飾器用于無條件地跳過某個測試函數或測試類。這通常用于標記那些暫時不相關、尚未完成或由于特定原因不應執行的測試。
語法:
@pytest.mark.skip(reason="跳過原因")
示例:
import pytestdef add(a, b):return a + bclass TestSkip:@pytest.mark.skip(reason="此功能正在重構,暫時跳過測試")@pytest.mark.apidef test_old_api_endpoint(self):assert add(1, 2) == 3
- skipif:有條件跳過
@pytest.mark.skipif裝飾器允許我們根據特定條件來決定是否跳過測試。如果條件為真,則測試被跳過;否則,測試正常執行。這在跨平臺測試、依賴特定環境或版本時非常有用。
語法:
@pytest.mark.skipif(condition, reason="跳過原因")
示例:
import pytest
import sysdef add(a, b):return a + bclass TestSkipIf:@pytest.mark.skipif(sys.version_info < (3, 9), reason="此測試需要Python 3.9或更高版本")@pytest.mark.webdef test_new_python_feature(self):assert add("Python", "3.9+") == "Python3.9+"@pytest.mark.skipif(True, reason="這是一個永遠跳過的示例")@pytest.mark.webdef test_always_skip(self):assert add("a", "b") == "ab"
- xfail:預期失敗
@pytest.mark.xfail裝飾器用于標記那些我們預期會失敗的測試用例。當一個被標記為xfail的測試實際失敗時,它不會被報告為錯誤,而是顯示為“預期失敗”(xfailed)。如果一個xfail測試意外地通過了,它會被報告為“預期通過”(xpassed)。這對于跟蹤已知bug、尚未修復的功能或正在開發中的特性非常有用。
語法:
@pytest.mark.xfail(condition=None, reason="預期失敗原因", raises=None, run=True, strict=False)
常用參數:
?reason:說明預期失敗的原因。
?raises:指定預期失敗時拋出的異常類型。如果測試失敗但未拋出指定異常,則會被報告為意外通過。
?strict:如果設置為True,當xfail測試意外通過時,Pytest會將其報告為失敗。默認為False。
示例:
import pytestdef divide(a, b):return a / bclass TestXfail:@pytest.mark.xfail(reason="除數為零,預期會拋出ZeroDivisionError")@pytest.mark.paydef test_divide_by_zero_xfail(self):# 預期此操作會引發ZeroDivisionErrorassert divide(10, 0) == 0 # 這個斷言會失敗,但因為xfail而被標記為預期失敗@pytest.mark.xfail(raises=TypeError, reason="預期類型錯誤,例如字符串和數字相加")@pytest.mark.paydef test_type_error_xfail(self):# 預期此操作會引發TypeErrorassert 1 + "a" # 這會引發TypeError@pytest.mark.xfail(strict=True, reason="此功能正在開發中,如果意外通過則視為錯誤")def test_feature_in_progress_xfail(self):# 模擬一個正在開發中且目前預期會失敗的功能assert False # 模擬失敗
- parametrize:參數化
@pytest.mark.parametrize是Pytest中用于數據驅動測試的核心標記。它允許我們使用不同的數據集重復執行同一個測試函數,從而提高測試的覆蓋率和效率。
語法:
@pytest.mark.parametrize(argnames, argvalues, ids=None, scope=None)
常用參數:
?argnames:一個字符串,包含用逗號分隔的參數名稱。
?argvalues:一個列表,包含參數值的元組或列表。每個元組/列表對應一次測試執行的參數組合。
?ids:一個字符串列表,用于為每個參數組合生成更具可讀性的測試ID。
示例:
import pytestdef multiply(a, b):return a * b@pytest.mark.parametrize("num1, num2, expected_result", [(1, 2, 2),(3, 4, 12),(5, 0, 0),(-1, 2, -2),
], ids=["positive_multiplication", "another_positive", "multiply_by_zero", "negative_multiplication"])
def test_multiply_operation(num1, num2, expected_result):assert multiply(num1, num2) == expected_result
一個實例:
import pytestdef add(a, b):return a + bclass TestAdd:@pytest.mark.skip@pytest.mark.apidef test_int(self):assert add(1, 2) == 3@pytest.mark.skipif(1 == 2,reason = "Java太難學了")@pytest.mark.webdef test_str(self):assert add("1", "2") == "12"@pytest.mark.xfail@pytest.mark.paydef test_list(self):assert add([1] ,[2,3,4,5]) == [1,2,3,4,5]@pytest.mark.xfail@pytest.mark.paydef test_list1(self):assert add([1] ,[2,3,4,5]) != [1,2,3,4,5]
結果:
三、數據驅動測試參數
在實際項目中,為了更好地管理和維護測試數據,通常會將測試數據存儲在外部文件中,例如CSV、Excel或JSON格式。Pytest結合參數化(parametrize)功能,可以方便地實現數據驅動測試,即通過外部數據文件來驅動測試用例的執行數量和內容。
我們可以編寫一個輔助函數來讀取這些外部數據,并將其作為@pytest.mark.parametrize裝飾器的參數值(argvalues)。
1. 數據文件示例:data.csv
a, b, c
1, 1, 1
2, 3, 5
3, 3, 6
4, 4, 7
2. 測試代碼示例置文件
@pytest.mark.ddt
@pytest.mark.parametrize("a, b, c",read_csv("data.csv")
)
def test_ddt(self, a, b, c):res = add(int (a),int(b))assert res == int(c)
在上述test_ddt測試用例中,read_csv("data.csv")會讀取data.csv中的每一行數據,并將其作為a、b、c的參數值傳遞給測試函數。每次執行時,a和b會被轉換為整數進行add操作,然后結果與轉換后的c進行斷言。
3. 配置文件:pytest.ini
為了更好地管理自定義標記,例如本例中的ddt標記,建議在pytest.ini文件中進行注冊。這有助于避免Pytest的警告,并提供標記的清晰描述。
[pytest]markers =api:接口測試web:UI測試ut:單元測試login:登錄相關pay:支付相關ddt:數據驅動測試
pytest.ini中注冊ddt標記,我們可以使用pytest -m ddt命令來專門執行所有數據驅動測試用例。
總結
通過本文的詳細闡述,我們深入了解了Pytest的配置機制和強大的標記功能。合理利用命令行參數、pytest.ini配置文件以及各種內置和自定義標記,可以極大地提升測試的組織性、可控性和執行效率。掌握這些高級特性,將幫助您構建更專業、更高效的Python測試實踐。