21. 自动化测试框架开发之Excel配置文件的测试用例改造
一、测试框架核心架构
1.1 组件依赖关系
# 核心库依赖
import unittest # 单元测试框架
import paramunittest # 参数化测试扩展
from chap3.po import * # 页面对象模型
from file_reader import ExcelReader # Excel数据读取器
# 必须使用的库版本
# xlrd==1.2.0(支持xlsx格式)
二、Excel数据驱动实现
2.1 数据加载配置
# Excel数据结构示例
# | name | password | assertion | message |
# |--------|----------|------------------------|-----------------------|
# | Tester | test | Invalid Login or Password | test_login_admin passed |
data = ExcelReader(
r'E:\Py3Sel3Ifram\chap5\Demo.xlsx',
sheet=0, # 第一个工作表
excel_title=True # 首行为标题
).data
参数说明:
- sheet=0:读取第一个工作表
- excel_title=True:将首行作为字典键
- 文件路径需使用原始字符串(r前缀)
三、测试用例参数化方案
3.1 使用paramunittest实现(注释方案)
@paramunittest.parametrized(data[1],data[2],data[3])
class TestOder(unittest.TestCase, Oder):
def setParameters(self, name, pwd, ass, txt):
"""参数映射方法"""
self.name = name
self.pwd = pwd
self.ass = ass
self.txt = txt
def test_login(self):
"""测试执行方法"""
self.get()
self.login(self.name, self.pwd)
sleep(2)
assert self.element('op.invalid_login').text == self.ass
print(self.txt)
方案特点:
- 每个测试用例独立执行
- 需要明确指定数据索引
- 自动生成多个测试方法
3.2 使用subTest实现(当前方案)
class TestLogin(unittest.TestCase, Oder):
def test_login(self):
for d in data:
with self.subTest(d): # 子测试上下文
self.get()
self.login(d['name'], d['password'])
sleep(2)
try:
self.assertEqual(
self.element('op.invalid_login').text,
d['assertion']
)
except AssertionError:
self.driver.save_screenshot(f'./{d["assertion"]}.png')
raise
print(d['message'])
方案优势:
- 单测试方法管理多组数据
- 失败时继续执行后续用例
- 自动生成详细测试报告
四、关键技术点解析
4.1 数据驱动流程
开始
├─ 读取Excel测试数据
├─ 遍历数据集合
│ ├─ 初始化浏览器
│ ├─ 执行登录操作
│ ├─ 验证断言结果
│ ├─ 成功:输出日志
│ └─ 失败:截图保存
└─ 生成测试报告
4.2 异常处理机制
try:
self.assertEqual(actual, expected)
except AssertionError:
self.driver.save_screenshot(f'./{d["assertion"]}.png') # 失败截图
raise # 重新抛出异常
功能特点:
- 精确捕获断言失败
- 自动保存错误现场
- 保留原始异常堆栈
五、Excel数据规范要求
5.1 数据结构标准
列名 | 类型 | 说明 |
---|---|---|
name | str | 用户名输入 |
password | str | 密码输入 |
assertion | str | 预期断言文本 |
message | str | 测试结果描述 |
5.2 格式注意事项
- 第一行必须为标题行
- 各列顺序需与代码参数对应
- 文本型断言值需完全匹配
- 避免使用特殊字符作为文件名
六、工程实践建议
6.1 数据管理策略
# 建议文件结构
testdata/
├─ login/
│ ├─ valid_login.xlsx
│ └─ invalid_login.xlsx
├─ order/
│ └─ create_order.xlsx
6.2 执行效率优化
优化策略 | 实现方式 | 效果预估 |
---|---|---|
浏览器复用 | 使用setUpClass/tearDownClass | 减少80%启动时间 |
智能等待 | 显式等待替代固定sleep | 提升30%执行速度 |
并行执行 | 使用pytest-xdist插件 | 线性提升效率 |
七、版本兼容性说明
7.1 必须环境配置
# 安装指定版本库
pip install xlrd==1.2.0 paramunittest==1.0.2
# 版本冲突说明:
# xlrd>=2.0.0 不支持xlsx格式
# paramunittest需要兼容Python3.13
7.2 常见错误处理
错误现象 | 原因分析 | 解决方案 |
---|---|---|
无法打开xlsx文件 | xlrd版本不正确 | 降级到1.2.0版本 |
参数映射失败 | Excel列名不匹配 | 检查标题行命名 |
元素定位超时 | 页面加载缓慢 | 添加显式等待机制 |
截图保存失败 | 路径权限问题 | 使用绝对路径或检查权限 |
八、完整代码
"""
Python :3.13.3
Selenium: 4.31.0
"""
import unittest
import paramunittest
from time import sleep
from chap3.po import *
from .file_reader import ExcelReader
# 处理 collections.Mapping 的兼容性问题
import collections
try:
collections.Mapping
except AttributeError:
import collections.abc
collections.Mapping = collections.abc.Mapping
# 获取并解析Excel数据
# data = ExcelReader(r'E:\Py3Sel3Ifram\chap5\Demo.xlsx',
# sheet=0,
# excel_title=False).data
# @paramunittest.parametrized(data[1],data[2],data[3])
# class TestOder(unittest.TestCase, Oder):
# def setParameters(self, name, pwd, ass, txt):
# self.name = name
# self.pwd = pwd
# self.ass = ass
# self.txt = txt
#
# def test_login(self):
# self.get()
# self.login(self.name, self.pwd)
# sleep(2)
# assert self.element('op.invalid_login').text == self.ass
# print(self.txt)
# 获取并解析Excel数据
data = ExcelReader(r'E:\Py3Sel3Ifram\chap5\Demo.xlsx',
sheet=0).data
# data = (
# {'name': 'Testerr', 'pwd': 'test', 'ass': 'Invalid Login or Password.', 'txt': 'test_login_admin is passed'},
# {'name': 'Sam', 'pwd': 'test', 'ass': 'Invalid Login or Password.', 'txt': 'test_login_Sam is passed'},
# {'name': 'Tom', 'pwd': 'test', 'ass': 'Invalid Login or Password..', 'txt': 'test_login_Tom is passed'}
# )
class TestLogin(unittest.TestCase, Oder):
def test_login(self):
for d in data:
with self.subTest(d):
self.get()
self.login(d['name'], d['password'])
sleep(2)
# assert self.element(self.invalid_login).text == d['ass'], \
# self.driver.save_screenshot(f'./{d["ass"]}.png')
try:
self.assertEqual(self.element('op.invalid_login').text, d['assertion'])
except AttributeError:
self.driver.save_screenshot(f'./{d["assertion"]}.png')
raise AssertionError
print(d['message'])
if __name__ == '__main__':
unittest.main()
Excel的数据:
最佳实践建议:建议将测试数据与测试代码分离管理,使用独立的testdata目录存储各类测试数据文件。实际项目数据统计显示,采用数据驱动模式可降低60%的测试维护成本。
「小贴士」:点击头像→【关注】按钮,获取更多软件测试的晋升认知不迷路! 🚀