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集成测试基础框架 + 模拟器闭环测试

相关推荐
芝士爱知识a1 小时前
【FinTech前沿】重塑衍生品交易:十维深度解析 AlphaGBM 智能期权分析平台
人工智能·python·量化交易·期权分析·alphagbm·期权交易·ai期权
小白菜又菜2 小时前
Leetcode 235. Lowest Common Ancestor of a Binary Search Tree
python·算法·leetcode
Omigeq2 小时前
1.2.2 - 采样搜索算法(以RRT和RRT*为例) - Python运动规划库教程(Python Motion Planning)
开发语言·人工智能·python·机器人
凌云拓界2 小时前
TypeWell全攻略(二):热力图渲染引擎,让键盘发光
前端·后端·python·计算机外设·交互·pyqt·数据可视化
小白菜又菜2 小时前
Leetcode 234. Palindrome Linked List
python·算法·leetcode
恒云客2 小时前
python uv debug launch.json
数据库·python·json
Katecat996632 小时前
YOLO11-SEG-AFPN-P345改进采血装置检测与识别系统
python
q1234567890982 小时前
FNN sin predict
开发语言·python
先做个垃圾出来………3 小时前
Python字节串“b“前缀
开发语言·python
dreams_dream3 小时前
什么是迭代器和生成器
python