一、?pytest自動生成測試類 demo
# -*- coding:utf-8 -*-
# @Author: 喵醬
# @time: 2023 - 08 -15
# @File: test4.py
# desc:
import pytest
import unittest# 動態生成測試類def create_test_class(class_name:str, test_cases:list) -> type:"""生成測試類:param class_name: 測試類的類名,是一個字符串:param test_cases: 是一個可迭代的對象(list),每一個元素case是一個字典在pytest測試類中,每一個方法,就是一條case,所以這里將多條case數據,生成測試類的多個方法:return: 測試類,包含多個方法(測試case)"""# 使用 type() 函數動態創建一個繼承自 unittest.TestCase 的子類test_class = type(class_name, (unittest.TestCase,), {})for case in test_cases:test_method = generate_test_method(case)# 動態為測試類添加測試方法setattr(test_class, test_method.__name__, test_method)return test_class# 生成測試方法
def generate_test_method(case: dict) -> callable:"""生成單個測試方法。Args:case (dict): 測試用例字典,包含輸入和輸出的信息。Returns:callable: 生成的測試方法。"""def test_method(self):"""實際執行測試的方法。"""assert case["input"] == case["output"]# 設置測試方法的名稱test_method.__name__ = f"test_{case['name']}"return test_method# 定義測試數據
@pytest.fixture(params=[{"name": "case1", "input": 1, "output": 1},{"name": "case2", "input": 3, "output": 3},
])
def test_case(request):return request.param# 測試生成的測試類
@pytest.mark.usefixtures("test_case")
def test_generator(test_case):test_class = create_test_class("DynamicTestClass", [test_case])# 加載測試類,并創建測試套件suite = unittest.TestLoader().loadTestsFromTestCase(test_class)# 運行測試套件result = unittest.TextTestRunner().run(suite)# 斷言測試結果是否成功assert result.wasSuccessful()
執行?test_generator 方法
運行順序:
1、手動觸發運行??test_generator 方法
2、在執行test_generator 方法 方法之前,會先執行test_case 方法。
3、test_case 方法,數據有2條數據,則test_case 方法會執行兩次
4、test_generator 同樣也會執行兩次。
最終效果,就是生成了兩次名為?DynamicTestClass 的測試類,每個測試類,只生成了一個方法(case)
?二、代碼詳解
2.1 整體運行順序
1、定義了一個函數 create_test_class,用于動態生成測試類。
2、定義了一個函數 generate_test_method,用于生成單個測試方法。
3、使用了 @pytest.fixture 裝飾器定義了一個夾具(fixture) test_case,用于提供測試數據。
4、使用了 @pytest.mark.usefixtures("test_case") 裝飾器標記了一個測試函數 test_generator,表示在執行該測試函數之前需要先執行 test_case 夾具。
5、在函數 test_generator 中,調用了 create_test_class 函數,傳入一個測試數據。然后加載并運行生成的測試類。
6、執行測試時,先執行夾具函數 test_case 來獲取測試數據。
7、根據測試數據,動態生成一個測試類 DynamicTestClass,并為該類添加一個測試方法。
8、創建測試套件,并運行測試套件中的測試用例。
9、使用 unittest.TextTestRunner 運行測試套件,并返回測試結果。
10、最后,根據測試結果判斷是否成功,并進行斷言。
總結起來,整個代碼的執行流程是:先執行夾具函數獲取測試數據,然后動態生成測試類并創建測試套件,最后運行測試套件中的測試用例,輸出測試結果,并進行斷言判斷。
2.1 定義測試數據
# 定義測試數據
@pytest.fixture(params=[{"name": "case1", "input": 1, "output": 1},{"name": "case2", "input": 3, "output": 3},
])
def test_case(request):return request.param
1、@pytest.fixture 是 pytest 框架提供的裝飾器,用于定義夾具(fixture)。夾具是在測試函數或測試類執行前后進行特定操作、提供數據等的函數。
2、在這段代碼中,test_case 是一個夾具函數。通過 @pytest.fixture 裝飾器對其進行標記,表示它是一個夾具。
3、params 參數是用來定義多個測試用例數據的列表。每個測試用例數據都是一個字典,包含了三個鍵值對:name、input 和 output。name 是用來描述測試用例的名稱,input 是輸入數據,output 是預期輸出結果。
4、當執行測試函數時,pytest 會自動調用夾具函數,并根據參數列表中的每個字典生成不同的測試數據。每個測試用例都會獨立執行一次測試函數,并且將對應的測試數據作為參數傳遞給測試函數。
在這段代碼中,夾具函數 test_case 返回 request.param,request.param 表示當前測試用例的數據。當執行 test_generator 函數時,會先調用夾具函數 test_case 獲取測試數據,然后將該測試數據作為參數傳遞給 test_generator 函數。
2.2?運行生成的測試類?test_generator
# 測試生成的測試類
@pytest.mark.usefixtures("test_case")
def test_generator(test_case):test_class = create_test_class("DynamicTestClass", [test_case])# 加載測試類,并創建測試套件suite = unittest.TestLoader().loadTestsFromTestCase(test_class)# 運行測試套件result = unittest.TextTestRunner().run(suite)# 斷言測試結果是否成功assert result.wasSuccessful()
1、裝飾器:?@pytest.mark.usefixtures("test_case")
@pytest.mark.usefixtures("test_case") 是 Pytest 測試框架中一個裝飾器標記(decorator marker),用于指定在運行測試用例之前需要先執行的 fixture(夾具)。
fixture 是一種在測試運行之前準備測試環境、數據或者資源的機制。它可以用來為測試用例提供必要的前置條件,例如設置數據庫連接、創建臨時文件、初始化測試數據等。
在給定的代碼中,@pytest.mark.usefixtures("test_case") 將 "test_case" 作為 fixture 的名稱進行標記。這意味著在運行使用了該標記的測試用例之前,Pytest 將先執行名為 "test_case" 的 fixture 方法。
使用 @pytest.mark.usefixtures 標記可以方便地應用 fixture,而無需在每個測試用例函數中顯式調用 fixture。通過將標記應用于測試用例函數,測試框架將自動處理 fixture 的執行和銷毀,并將其提供給測試用例函數作為參數使用。
總之,@pytest.mark.usefixtures("test_case") 允許你在測試運行之前自動執行名為 "test_case" 的 fixture 方法,為測試用例提供必要的準備工作。
2.3?generate_test_method
generate_test_method
函數中的 case
參數是一個字典類型。在示例代碼中,test_case
是一個包含測試用例信息的字典,其中包括字段 "name"
、"input"
和 "output"
,用于表示測試名稱、輸入和期望輸出。
傳遞給 generate_test_method
函數的 case
參數應該具有與示例中定義的測試用例字典相同的結構。例如:
case = {"name": "example", "input": 1, "output": 2}
test_method = generate_test_method(case)
在這個示例中,case
是一個字典,表示一個測試用例,包含 "name"
、"input"
和 "output"
字段。generate_test_method
函數將根據這些字段生成一個測試方法,并為該方法設置名稱。
2、create_test_class
在函數 create_test_class
中,test_cases
參數的類型可以是任何可迭代對象,比如列表或元組。
示例代碼中使用了迭代循環來遍歷 test_cases
,假設 test_cases
是一個列表,每個元素都是一個測試用例字典。所以 test_cases
的結構可以類似以下的形式:
test_cases = [{"name": "case1", "input": 1, "output": 2},{"name": "case2", "input": 3, "output": 3},
]
其中,每個 test_cases
列表的元素都是一個包含測試用例信息的字典。
所以,在調用 create_test_class
函數時,你可以傳遞一個包含測試用例字典的列表作為 test_cases
參數,動態生成相應的測試類。