Python 单元测试数据驱动实战:结合 JSON 与 Parameterized
在自动化测试中,数据驱动测试(Data-Driven Testing)能够将测试逻辑与测试数据分离,从而极大提升用例的可维护性与覆盖率。本文将演示如何基于 unittest 框架,结合 parameterized 库与外部 JSON 文件,构建一个完整的数据驱动测试流程。
项目结构设计
为了实现高内聚低耦合,我们将项目划分为业务代码、配置文件、测试数据、工具类以及测试用例模块。推荐的目录结构如下:
project_root/
├── auth_service.py # 业务逻辑模块
├── config.py # 全局配置与路径管理
├── main.py # 测试执行与报告生成入口
├── data/
│ └── auth_scenarios.json # 外部测试数据源
├── utils/
│ └── data_loader.py # 数据解析工具
└── tests/
└── test_auth.py # 参数化测试用例
业务逻辑实现
首先定义一个待测试的用户认证服务。与直接返回字符串不同,这里采用返回状态字典的方式,更贴近真实 API 的响应结构。
# auth_service.py
def authenticate_user(user_id: str, secret_key: str) -> dict:
"""
模拟用户身份认证服务
"""
if user_id == 'sys_admin' and secret_key == 'S3cur3P@ss':
return {"status_code": 200, "message": "Authentication successful"}
return {"status_code": 401, "message": "Authentication failed"}
全局路径配置
为了避免硬编码路径带来的跨平台问题,使用 Python 内置的 pathlib 模块来动态获取项目根目录的绝对路径。
# config.py
from pathlib import Path
# 动态解析当前文件所在目录作为项目根节点
PROJECT_ROOT = Path(__file__).parent.resolve()
测试数据准备
将测试场景抽象为 JSON 格式,存放在 data 目录下。每个对象代表一个独立的测试用例,包含输入参数与预期断言结果。
// data/auth_scenarios.json
[
{
"scenario": "Valid credentials",
"user_id": "sys_admin",
"secret_key": "S3cur3P@ss",
"expected_code": 200
},
{
"scenario": "Invalid user ID",
"user_id": "guest",
"secret_key": "S3cur3P@ss",
"expected_code": 401
},
{
"scenario": "Invalid secret key",
"user_id": "sys_admin",
"secret_key": "wrong_password",
"expected_code": 401
},
{
"scenario": "Empty credentials payload",
"user_id": "",
"secret_key": "",
"expected_code": 401
}
]
数据加载器设计
创建一个工具函数,负责读取 JSON 文件并将其转换为 parameterized 库所需的元组列表格式。此处使用列表推导式简化了数据转换逻辑。
# utils/data_loader.py
import json
from config import PROJECT_ROOT
def load_auth_test_cases() -> list:
"""
从 JSON 文件中读取并格式化认证测试数据
"""
data_file_path = PROJECT_ROOT / 'data' / 'auth_scenarios.json'
with open(data_file_path, 'r', encoding='utf-8') as file:
raw_data = json.load(file)
# 提取所需字段并组装为元组序列
return [
(item['scenario'], item['user_id'], item['secret_key'], item['expected_code'])
for item in raw_data
]
编写参数化测试用例
在测试类中引入 parameterized.expand 装饰器。它会在测试收集阶段自动遍历数据列表,为每一条数据生成独立的测试方法。
# tests/test_auth.py
import unittest
from parameterized import parameterized
from auth_service import authenticate_user
from utils.data_loader import load_auth_test_cases
class TestAuthentication(unittest.TestCase):
@parameterized.expand(load_auth_test_cases())
def test_user_authentication(self, scenario, user_id, secret_key, expected_code):
"""
验证不同数据场景下的身份认证状态码
"""
response = authenticate_user(user_id, secret_key)
# 断言实际返回的状态码与期望值一致,并附带场景描述以便于失败排查
self.assertEqual(
response['status_code'],
expected_code,
f"Test failed for scenario: {scenario}"
)
测试执行与 HTML 报告生成
最后,通过编写主运行脚本,利用 unittest.TestLoader 自动发现测试用例,并借助 HtmlTestRunner 将执行结果输出为可视化的 HTML 报告。
# main.py
import unittest
from HtmlTestRunner import HTMLTestRunner
from config import PROJECT_ROOT
from tests.test_auth import TestAuthentication
if __name__ == '__main__':
# 动态加载测试套件
test_suite = unittest.TestLoader().loadTestsFromTestCase(TestAuthentication)
# 确保报告输出目录存在
report_dir = PROJECT_ROOT / 'reports'
report_dir.mkdir(exist_ok=True)
# 初始化测试运行器并配置报告属性
runner = HTMLTestRunner(
output=str(report_dir),
report_name='auth_test_execution_report',
report_title='Authentication Module Test Report',
descriptions=True
)
# 执行套件
runner.run(test_suite)
