智能座舱自动化测试框架工程 - 第4节
4. 测试框架工程基础
做智能座舱测试,选对框架等于成功了一半。我见过不少团队,一开始用 Unittest 写了几百个用例,后来发现维护成本越来越高,跑一次要半小时,最后不得不重构。嗯,今天我们就来聊聊这两个框架,以及我为什么最终选择了 Pytest。
4.1 Unittest:Python 自带的“老大哥”
Unittest 是 Python 标准库的一部分,开箱即用。它的设计思路参考了 Java 的 JUnit,所以如果你有 Java 背景,上手会很快。
核心特点:
- 不需要额外安装,Python 装好就有
- 强制使用类继承(TestCase)来组织用例
- 自带 setUp / tearDown 方法,用于前置和后置操作
- 断言方法丰富(assertEqual、assertTrue、assertIn 等)
但说实话,在智能座舱这种复杂场景下,Unittest 有点“不够用”。
4.2 Pytest:更现代、更灵活的选择
Pytest 不是标准库,需要 pip install pytest。但它带来的便利,绝对值回那几秒钟的安装时间。
核心特点:
- 函数式测试:不需要写类,直接写函数就行
- 自动发现测试:默认找
test_*.py或*_test.py文件 - 强大的 fixture 机制:替代 setUp/tearDown,更灵活
- 参数化测试:一组数据跑多个用例,代码量减半
- 丰富的插件生态:allure、xdist、ordering 等
4.3 对比表格:一图看懂差异
| 对比维度 | Unittest | Pytest |
|---|---|---|
| 安装方式 | 内置,无需安装 | pip install pytest |
| 用例组织 | 必须继承 TestCase 类 | 函数或类均可,更自由 |
| 前置后置 | setUp / tearDown(固定名称) | fixture(自定义名称,作用域可控) |
| 参数化 | 需要 subTest 或自己写循环 | @pytest.mark.parametrize,一行搞定 |
| 插件生态 | 较少 | 丰富(allure、xdist、ordering 等) |
| 报告生成 | 默认文本,可扩展 | pytest-html / allure,美观易读 |
| 智能座舱适用性 | 适合小型、简单项目 | 适合中大型、多场景项目 |
我个人习惯用 Pytest。为什么?因为智能座舱测试涉及的东西太多了——语音、导航、蓝牙、空调、仪表盘……每个模块都需要不同的前置条件。Pytest 的 fixture 能让我把这些条件拆开、复用,而不是堆在一个 setUp 里。
4.4 Pytest 核心特性详解
4.4.1 Fixture:你的测试“万能工具箱”
Fixture 是 Pytest 最强大的特性之一。说白了,它就是一段可以重复使用的代码,用来准备测试环境或清理测试数据。
基本用法:
import pytest
@pytest.fixture
def connect_car():
# 前置:连接座舱模拟器
print("连接座舱模拟器...")
simulator = Simulator.connect("192.168.1.100")
yield simulator # 把模拟器对象传给测试函数
# 后置:断开连接
print("断开座舱模拟器...")
simulator.disconnect()
def test_voice_control(connect_car):
# connect_car 就是上面返回的 simulator 对象
result = connect_car.send_voice_command("打开空调")
assert result.status == "success"
Fixture 的作用域:
function(默认):每个测试函数都执行一次class:每个测试类执行一次module:每个测试文件执行一次session:整个测试会话只执行一次
session 作用域。启动一次,所有用例共用。但要注意——如果模拟器崩溃了,后面的用例全挂。所以我会加一个心跳检测,每隔几个用例检查一次连接状态。
4.4.2 参数化:一套代码,多组数据
智能座舱测试中,经常要测同一个功能在不同条件下的表现。比如语音控制空调,温度从 16°C 到 30°C,每个温度都要测。用 Unittest 你得写 15 个几乎一样的函数。用 Pytest,一行搞定。
import pytest
@pytest.mark.parametrize("temperature, expected", [
(16, "16°C"),
(22, "22°C"),
(30, "30°C"),
(35, "30°C"), # 超出范围,应该取最大值
])
def test_set_temperature(temperature, expected, connect_car):
result = connect_car.set_ac_temperature(temperature)
assert result.display == expected
你想想看,如果不用参数化,这 4 个用例你得写 4 个函数。而且如果后面要加一个 18°C 的用例,参数化只需要加一行数据,非参数化你得复制粘贴整个函数。
4.4.3 插件生态:让测试更强大
Pytest 的插件系统,是我离不开它的另一个原因。这里列几个我在座舱测试中常用的:
| 插件名 | 用途 | 安装命令 |
|---|---|---|
| pytest-html | 生成 HTML 格式的测试报告 | pip install pytest-html |
| pytest-xdist | 并行执行测试,节省时间 | pip install pytest-xdist |
| pytest-ordering | 控制测试用例的执行顺序 | pip install pytest-ordering |
| pytest-rerunfailures | 失败用例自动重跑 | pip install pytest-rerunfailures |
| allure-pytest | 集成 Allure 报告,更美观 | pip install allure-pytest |
pytest -n 4 启动 4 个进程并行跑。我试过,原来跑 30 分钟的座舱全量回归测试,缩短到 8 分钟。但要注意——如果多个用例同时操作同一个资源(比如同时设置空调温度),可能会冲突。建议按模块分组,不同模块并行,同模块串行。
4.5 框架搭建实战:从零开始搭一个座舱测试框架
光说不练假把式。下面我带你搭一个最小可用的智能座舱测试框架。别怕,就几个文件。
4.5.1 项目结构
smart_cockpit_test/
├── conftest.py # 全局 fixture 配置
├── test_voice.py # 语音测试
├── test_navigation.py # 导航测试
├── test_ac.py # 空调测试
├── utils/
│ ├── __init__.py
│ └── simulator.py # 模拟器连接工具
├── data/
│ └── test_data.json # 测试数据
└── reports/ # 测试报告输出目录
4.5.2 conftest.py:全局 fixture 集中管理
import pytest
from utils.simulator import Simulator
@pytest.fixture(scope="session")
def simulator():
"""全局模拟器连接,整个测试只连接一次"""
sim = Simulator.connect("192.168.1.100")
yield sim
sim.disconnect()
@pytest.fixture
def reset_ac(simulator):
"""每个空调测试前,把空调重置到默认状态"""
simulator.set_ac_temperature(24)
simulator.set_ac_fan_speed(2)
yield
# 测试结束后不清理,留给下一个 fixture 处理
4.5.3 一个完整的测试文件:test_ac.py
import pytest
class TestAC:
"""空调功能测试"""
@pytest.mark.parametrize("temp, expected", [
(18, "18°C"),
(25, "25°C"),
(32, "30°C"), # 上限 30
])
def test_set_temperature(self, simulator, reset_ac, temp, expected):
"""测试设置空调温度"""
result = simulator.set_ac_temperature(temp)
assert result.display == expected
def test_ac_off(self, simulator):
"""测试关闭空调"""
simulator.turn_off_ac()
status = simulator.get_ac_status()
assert status == "off"
4.5.4 运行测试
# 运行所有测试
pytest
# 运行空调模块测试,生成 HTML 报告
pytest test_ac.py --html=reports/ac_report.html
# 并行运行(4 进程)
pytest -n 4
# 失败重跑(最多重跑 2 次)
pytest --reruns 2
pytest.ini 配置文件,把常用参数写进去,这样每次只要敲 pytest 就行。
# pytest.ini
[pytest]
addopts = -v --html=reports/report.html --self-contained-html
testpaths = tests
python_files = test_*.py
4.6 知识体系总览
下面这张图,帮你把本章的核心逻辑串起来。从框架选型到核心特性,再到实战搭建,一条线走通。
- Unittest 适合小项目,Pytest 适合智能座舱这种复杂场景
- Fixture 是 Pytest 的灵魂,学会用它管理前置后置操作
- 参数化能让你用一套代码覆盖多组数据,减少重复劳动
- 插件生态让测试更高效:并行、重跑、报告生成一步到位
- 实战搭建:conftest.py + 测试文件 + pytest.ini,一个最小框架就出来了