Pelco KBD300A 模拟器:19.pytest集成测试(serial + protocol + macro)

第 19 篇:集成测试(serial + protocol + macro)

现在是时候从单元测试 逐步过渡到集成测试阶段了。

我们已经完成了核心纯逻辑模块(协议构建/解析、宏语法、模板渲染、日志发射、规则引擎、模拟器状态机等)的单元测试,覆盖率已经相当不错。接下来需要验证这些模块组合在一起 时是否能正确协作,尤其是涉及串口 → 协议解析 → 命令分发 → 宏执行 → 协议构建 → 串口输出的完整链路。

以下是针对这个项目的集成测试规划建议(重点放在 serial + protocol + macro 链路),以及逐步实施路线。

集成测试目标(优先级排序)
优先级 测试目标 涉及模块链路 建议测试类型 难度
★★★★★ 1. 串口收发 → 协议解析 → 虚拟设备状态变化 serial.worker + protocol + virtual_device 模拟器集成
★★★★☆ 2. 宏命令 → 协议构建 → 串口发送 macro.commands + protocol + serial.manager/worker Mock串口发送
★★★★☆ 3. 完整宏脚本执行(包含循环/条件/延时) macro.engine + parser + commands + protocol 模拟器 + Mock
★★★☆☆ 4. 报警规则触发 → 执行宏/动作 alarm.rules + macro.engine + commands 模拟器触发
★★★☆☆ 5. 键盘面板 → 宏/PTZ命令 → 协议 → 发送 ui.keyboard.panel + serial + protocol UI + Mock串口
★★☆☆☆ 6. 真实串口收发(可选,后期) 全链路 + 真实设备或两台电脑互连 端到端 很高
推荐的集成测试技术栈与工具
目的 推荐工具/方式 理由 / 优点
模拟串口 pytest-mock + unittest.mockpyfake-serial 无需真实硬件,最快、最干净、可控
协议层 Mock 直接 mock serial.Serial.write / read 验证协议构建与解析是否匹配
虚拟设备注入 使用已有的 VirtualDevice 天然适合作为"下游"被测对象
宏执行环境 Mock mock MacroCommands 的硬件方法(如 ptz_control 隔离真实串口,专注宏逻辑
测试异步/线程 pytest-asynciopytest-qt + QTimer 处理 SerialWorker 的定时读取、宏引擎的 QThread
UI 集成测试(可选后期) pytest-qt + qtbot 可模拟按钮点击、摇杆拖动等,但前期可先跳过
逐步实施路线(大概 4~6 周完成核心部分)
第 1 周:搭建集成测试基础框架 + 模拟器闭环测试

目标:验证 串口收到数据 → 解析 → VirtualDevice 状态变化 → 返回响应 的完整链路

python 复制代码
# tests/integration/test_simulator_loop.py
import pytest
from unittest.mock import MagicMock, patch
from core.serial.worker import SerialWorker
from core.simulator.virtual_device import VirtualDevice
from core.protocol import get_protocol

@pytest.fixture
def virtual_device():
    return VirtualDevice(cam_id=1)

@pytest.fixture
def mock_serial():
    with patch("serial.Serial") as mock_ser:
        mock_ser_instance = mock_ser.return_value
        mock_ser_instance.is_open = True
        mock_ser_instance.read.side_effect = lambda size: b""  # 默认空
        yield mock_ser_instance

def test_receive_ptz_command_updates_virtual_device(qtbot, mock_serial, virtual_device):
    # 准备 Pelco-D 绝对移动命令(示例)
    cmd = bytes([0xFF, 0x01, 0x00, 0x04, 0x20, 0x20, 0x45])  # pan 32, tilt 32
    
    # 模拟收到数据
    worker = SerialWorker(port="COM3", baud=9600, protocol="D")
    worker._ser = mock_serial
    worker._buffer = bytearray(cmd)
    
    # 注入虚拟设备处理逻辑(实际项目中可通过 dependency injection)
    protocol = get_protocol(worker, "D")
    protocol._virtual_device = virtual_device  # 假设支持注入
    
    # 触发一次读取处理
    worker._process_buffer()
    
    status = virtual_device.get_status_dict()
    assert "pan" in status
    assert abs(float(status["pan"][:-1]) - 32.0 * some_scale_factor) < 1.0  # 根据实际比例断言
第 2~3 周:宏命令 → 协议构建 → 串口发送 集成

目标:运行简单宏 → 验证是否正确调用 serial_mgr.write() 并发出正确协议字节

python 复制代码
# tests/integration/test_macro_to_serial.py
from core.macro.engine import MacroEngine
from core.macro.commands import MacroCommands
from unittest.mock import MagicMock

@pytest.fixture
def mock_serial_manager():
    mgr = MagicMock()
    mgr.write = MagicMock()
    mgr.protocol = "D"
    return mgr

def test_macro_ptz_move_generates_correct_packet(mock_serial_manager):
    engine = MacroEngine(mock_serial_manager)
    commands = MacroCommands(mock_serial_manager)
    
    script = """
    ptz(1, 50, 30)      # 摄像机1,pan 50, tilt 30
    delay(100)
    stop_ptz(1)
    """
    
    engine.set_script(script)
    engine.run()
    
    # 验证是否调用了 write 两次(move + stop)
    assert mock_serial_manager.write.call_count >= 2
    
    # 进一步断言发出的字节(Pelco-D 格式)
    first_call = mock_serial_manager.write.call_args_list[0]
    packet = first_call[0][0]
    assert packet.startswith(b'\xFF')
    assert len(packet) == 7
    # ... 校验 checksum、cmd1/cmd2 等
第 4 周:完整宏脚本 + 虚拟设备闭环

目标:运行带循环/延时的宏脚本,验证虚拟设备状态最终是否符合预期

python 复制代码
def test_pattern_run_macro_updates_virtual_state():
    # 准备带 pattern_run 的脚本
    script = """
    pattern_run(1, 2)
    delay(5000)
    pattern_stop(1, 2)
    """
    
    device = VirtualDevice(1)
    engine = MacroEngine(mock_serial_manager_with_virtual_device)
    
    # 注入虚拟设备
    engine.commands._virtual_device = device
    
    engine.set_script(script)
    engine.run()
    
    # 等待宏执行完成(可使用 qtbot.wait + 信号)
    # 断言 device.pattern_running == False 等状态
第 5 周:报警规则 + 宏联动集成
python 复制代码
# tests/integration/test_alarm_macro_linkage.py
def test_alarm_triggers_macro():
    rules = load_alarm_rules()
    # 模拟收到报警码 1002
    execute_alarm_action(app_window_mock, 1002, rules)
    
    # 验证是否调用了 MacroEngine.run("emergency_shutdown.macro")
总结建议
  1. 优先级最高 :先把 模拟器闭环(收 → 解析 → 虚拟设备状态 → 响应)做通,这是最有价值的集成验证点。
  2. 第二优先:宏 → 协议构建 → Mock 串口发送(验证协议正确性)。
  3. 第三阶段:完整宏脚本 + 虚拟设备状态断言(最接近真实使用场景)。
  4. 工具推荐pytest + pytest-mock + pytest-qt(如果涉及 UI) + pytest-timeout(防止死循环)。
  5. 测试数据:准备一套标准化的 Pelco-D/P 命令字节样本(至少 20~30 种常见命令)作为 fixture。

👉上一篇 :按依赖顺序 + 复杂度由低到高逐步推进pytest单元测试

👉总目录:Python开发软键盘全程总览

👉下一篇:搭建pytest集成测试基础框架 + 模拟器闭环测试

相关推荐
2301_790300965 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
Wang201220135 小时前
芯片serdes phy vth下阈值过低,线缆干扰会识别成oob如何解决
集成测试
VCR__5 小时前
python第三次作业
开发语言·python
韩立学长5 小时前
【开题答辩实录分享】以《助农信息发布系统设计与实现》为例进行选题答辩实录分享
python·web
2401_838472515 小时前
使用Scikit-learn构建你的第一个机器学习模型
jvm·数据库·python
u0109272715 小时前
使用Python进行网络设备自动配置
jvm·数据库·python
工程师老罗5 小时前
优化器、反向传播、损失函数之间是什么关系,Pytorch中如何使用和设置?
人工智能·pytorch·python
Fleshy数模6 小时前
我的第一只Python爬虫:从Requests库到爬取整站新书
开发语言·爬虫·python
CoLiuRs6 小时前
Image-to-3D — 让 2D 图片跃然立体*
python·3d·flask
小鸡吃米…6 小时前
机器学习 —— 训练与测试
人工智能·python·机器学习