Pelco KBD300A 模拟器:TEST01.重构后键盘部分的测试方案规划

重构后键盘部分的测试方案规划

在博客系列的《6.5 串口实现的逻辑优化、配置管理与协议完善》完成后,项目左侧键盘部分(包括 UI 布局、按键交互、LCD 显示、指示灯反馈和串口集成)已趋于成熟,但测试一直是项目中的缺失环节。早期单文件版(KBD300A_main.py)仅依赖手动运行验证,重构后多文件结构虽提升了可测试性,但缺乏系统化测试会导致潜在 bug(如按键信号丢失或串口线程异常)在生产环境中暴露。测试不仅是验证功能正确性,还能提升代码质量、便于 CI/CD 和未来维护。

本规划聚焦重构后键盘部分的全面测试方案,覆盖单元测试(单个组件)、集成测试(模块间交互)、端到端测试(全链路)和手动测试(手感/UI)。基于 Python 3.7(Windows 7 兼容),使用 pytest 框架(pip install pytest==7.4.0,兼容旧系统)和 mock(unittest.mock)模拟依赖。目标覆盖率 80%+,测试目录为 tests/(新增到项目根)。这一方案可作为博客新增篇目(如6.7:重构后键盘测试方案与实践),承接6.6的键盘优化,过渡到7篇的宏系统。规划分类型展开,每类包含目标、工具、步骤、代码示例和注意事项。

1. 测试总体原则与准备
  • 原则:TDD(测试驱动开发)优先(先写测试再优化代码);黑盒/白盒结合(功能 vs 内部逻辑);自动化为主,手动为辅。Win7 兼容:测试环境用 virtualenv,模拟旧硬件(e.g., COM 端口)。
  • 工具
    • pytest:单元/集成测试框架,支持 fixtures 参数化。
    • unittest.mock:模拟信号/串口(e.g., mock SerialManager.write)。
    • coverage.py(pip install coverage==6.5.0):测覆盖率(coverage run -m pytest;coverage report)。
    • PyQt5 测试:QTest(模拟按键/鼠标),但 Win7 用 QApplication.testTimeOut 防超时。
  • 准备
    1. 新建 tests/keyboard/:test_panel.py(KeyboardPanel)、test_integration.py等。
    2. requirements-dev.txt:pytest, coverage, mock。
    3. fixtures:pytest fixture 模拟 QApplication(@pytest.fixture def app(): app = QApplication([]); yield app; app.quit())。
    4. 运行:pytest tests/keyboard/ --cov=ui/keyboard --cov-report=html(生成报告)。
  • 覆盖范围:键盘模块(panel.py/lcd.py/joystick.py/indicator_manager.py)和集成(main_window.py 的键盘部分)。
  • 目标覆盖率:单元 90%、集成 70%、端到端 50%(手动补)。
2. 单元测试(单个组件隔离测试)

目标:验证独立功能(如按键回调、LCD 显示),不依赖外部(串口/信号)。快速运行,捕获边缘 case(如输入溢出)。

  • 步骤

    1. 测试 _on_digit:模拟数字输入,检查 input_buffer 和 lcd.display 调用。
    2. 测试 _set_mode:检查 mode 更新、lcd.display_text 调用和 mode_changed emit。
    3. 测试 LCD display_text:mock theme,检查 _mode_label.setText/setStyleSheet。
    4. 测试 IndicatorManager flash:用 mock.patch("PyQt5.QtCore.QTimer") 验证定时 off。
    5. 测试 Joystick mouseMoveEvent:模拟 pos,检查 pan_tilt_changed emit 值。
  • 代码示例(test_panel.py,从最终代码推导;用 code_execution 验证语法无误):

    python 复制代码
    # tests/keyboard/test_panel.py
    # 目标:测试单个文件(如 panel.py 的 _on_digit),不跑 GUI/串口。快速反馈。
    import pytest
    from unittest.mock import patch, MagicMock, call
    from ui.keyboard.panel import KeyboardPanel
    
    
    @pytest.fixture
    def keyboard(qapp):
        panel = KeyboardPanel()
        yield panel
        panel.close()
    
    
    def test_on_digit_limit(keyboard):
        # mock lcd.display
        with patch.object(keyboard.lcd, 'display') as mock_display:
            for i in range(5):  # 输入5位
                keyboard._on_digit(1)
            assert len(keyboard.input_buffer) == 4  # 限长4
            mock_display.assert_has_calls([call(1), call(11), call(111), call(1111)], any_order=False)
    
    
    def test_set_mode(keyboard):
        keyboard.mode_changed = MagicMock()
        with patch.object(keyboard.lcd, 'display_text') as mock_display_text:
            keyboard._set_mode("PRESET")
        assert keyboard.mode == "PRESET"
        keyboard.mode_changed.emit.assert_called_with("PRESET")
        mock_display_text.assert_called_once_with("PRESET", is_mode=True, mode_key="MODE_PRESET")
    ......
    # Win7 兼容:无特殊,pytest 运行正常
  • 注意事项:Win7 用 --no-qt-logging 防 Qt 日志干扰。mock theme dict 模拟 dark/light。

  • 执行 :pytest tests/keyboard/test_panel.py(预期 100% 通过,覆盖 on* 方法)。

3. 集成测试(模块间交互测试)

目标:验证键盘内部集成(如按键触发 LCD/灯)和与核心的桥接(信号 emit 到串口)。模拟依赖(如 mock SerialManager)。

  • 步骤

    1. 测试 _on_enter:mock preset_requested.emit,检查 try-except(异常时 ERR on)。
    2. 测试模式切换集成:_set_mode 后,检查 lcd.display_text 和 indicator_manager(如果模式灯扩展)。
    3. 测试指示灯与日志:mock _on_log_entry,检查 flash("TX") 调用 _style_on 和 QTimer。
    4. 测试摇杆与串口:mock joystick_moved.connect,模拟 moveEvent 检查 ptz_control 调用。
    5. 测试配置加载:mock json.load,检查 SerialManager 初始化参数。
  • 代码示例(test_integration.py):

    python 复制代码
    # tests/keyboard/test_integration.py
    # 目标:测试键盘与核心/mock 串口的交互(如按键 emit → 串口 write)。
    import pytest
    from unittest.mock import patch, MagicMock
    from ui.main_window import AppWindow
    
    
    @pytest.fixture
    def full_window(qapp):
        # 禁用 QSS 加载,避免测试环境崩溃
        with patch.object(AppWindow, "_apply_qss", lambda self, theme: None):
            win = AppWindow()
            win.show()
            qapp.processEvents()
            # mock serial_mgr._thread.isRunning
            win.serial_mgr._thread = MagicMock()
            win.serial_mgr._thread.isRunning.return_value = True
            yield win
            win.close()
    
    
    def test_joystick_ptz_control(full_window):
        # patch _safe_call,让它直接调用目标函数
        with patch.object(full_window, "_safe_call", lambda func, *a, **kw: func(*a, **kw)), \
             patch("ui.main_window.ptz_control") as mock_ptz, \
             patch("ui.main_window.ptz_stop") as mock_stop:
            full_window._on_joystick_moved(10, 20)
            mock_ptz.assert_called_once_with(full_window.serial_mgr,
                                             cam_id=1, pan_speed=10, tilt_speed=-20)
    
            full_window._on_joystick_moved(0, 0)
            mock_stop.assert_called_once_with(full_window.serial_mgr, cam_id=1)
    .......
    # Win7 兼容:mock 串口端口列表,确保 COM 扫描正常
  • 注意事项:用 @patch.multiple 批量 mock(e.g., themes.get_current_theme)。Win7 测试真实串口(COM3),检查 open 无异常。

  • 执行:pytest --cov=ui/keyboard tests/keyboard/(覆盖率报告 htmlcov/index.html)。

4. 端到端测试(全链路测试)

目标:模拟用户操作,验证键盘 → 信号 → 核心 → 反馈的全流程(如按键触发串口写 + 灯闪)。

  • 步骤

    1. 测试按键链路:用 QTest.keyClick 模拟 ENT,按无效输入检查 ERR 灯 + 日志。
    2. 测试串口反馈:mock VirtualDevice 生成响应,检查 parsed_received 后 RX 闪 + LCD 更新(如果模式联动)。
    3. 测试模式切换:模拟 PRESET 按,检查 _set_mode → lcd "PRESET" + 右面板 tab 切换(main_window 集成)。
    4. 测试异常:模拟 serial.SerialException,检查 error.emit + ERR on。
    5. 性能测试:长按键序列(100次),检查无延迟(线程支持)。
  • 代码示例(test_e2e.py,用 PyQt5.QtTest):

    python 复制代码
    # tests/test_e2e.py
    # 目标:模拟用户操作,验证 UI → 核心 → 反馈。
    import pytest
    from PyQt5 import QtWidgets
    from PyQt5.QtTest import QTest
    from PyQt5.QtCore import Qt
    
    from ui.main_window import AppWindow
    
    
    @pytest.fixture
    def window(qapp):
        """提供一个完整的 AppWindow 实例,并在测试结束后关闭"""
        win = AppWindow()
        win.show()
        qapp.processEvents()
        yield win
        win.close()
    
    
    def _find_button(kb, text):
        """辅助函数:根据按钮文字查找 QPushButton"""
        btns = kb.findChildren(QtWidgets.QPushButton)
        return next((b for b in btns if b.text() == text), None)
    
    
    def test_full_keyboard_flow(window):
        kb = window.keyboard
    
        # 模拟数字输入 1-2-3
        for digit in "123":
            target_btn = _find_button(kb, digit)
            assert target_btn is not None, f"Button {digit} not found"
            QTest.mouseClick(target_btn, Qt.LeftButton)
    ......
    # Win7 兼容:QTest 稳定,测试真实 COM 端口
  • 注意事项:端到端需 GUI 环境(pytest-qt 插件,pip install pytest-qt==4.4.0)。Win7 模拟旧硬件(用 loopback 串口测试)。

  • 执行:pytest -v tests/test_e2e.py(慢测试,标记 @pytest.mark.slow)。

5. 手动测试与工具集成

目标:覆盖自动化难测部分(如手感、视觉)。

  • 步骤
    1. 手感测试:运行 app.py,按数字检查 LCD 实时更新;摇杆拖拽检查平滑(无卡顿)。
    2. 视觉测试:切换 dark/light,检查 LCD/灯颜色;resize 窗口检查布局不崩。
    3. 异常手动:断开串口,检查 ERR 灯 + 弹窗。
    4. Win7 特定:多显示器测试(布局适应);旧 COM 端口(USB-RS232 转换器)验证。
  • 工具:Monkey 测试(随机按键);UI 录屏(OBS Studio 捕获手感 demo)。
  • 注意事项:文档测试 case(Excel:输入/预期/实际)。目标:无 crash,响应<100ms。
  • 执行:手动 checklist,CI 配置(pytest + coverage)。
6. 测试集成到项目
  • 项目添加:tests/ 目录 + .CI 配置(pytest + coverage)。
  • 覆盖率目标:用 coverage html 查看热图,优先高风险(如串口 parse)。
  • 益处:测试暴露重构遗漏(如信号未连),确保最终版稳定。

这一方案全面覆盖键盘测试,填补项目缺失。

上一篇 总目录 下一篇

相关推荐
守城小轩14 小时前
Chromium 142 编译指南 macOS篇:配置 depot_tools(三)
自动化·chrome devtools·浏览器自动化·指纹浏览器·浏览器开发
职业码农NO.114 小时前
开源:AI+无人机巡检系统项目调研
人工智能·python·开源·无人机·智能识别·无人机巡检
大刘讲IT14 小时前
工业AI认知失调与确定性重构:AI缺乏主观判断与逻辑纠错能力的深度剖析及防御体系构建研究
人工智能·经验分享·ai·重构·制造
熊猫钓鱼>_>14 小时前
对话式部署实践:从零开始使用TRAE SOLO构建自动化CI/CD Pipeline
运维·ci/cd·自动化·devops·trae·solo·trae solo
ttod_qzstudio14 小时前
从 Switch-Case 到自注册工厂:优雅的驱动行为管理系统重构
重构·typescript·工厂模式
lifewange1 天前
UI自动化页面元素定位有几种方式
前端·ui·自动化
ada7_1 天前
LeetCode(python)78.子集
开发语言·数据结构·python·算法·leetcode·职场和发展
我送炭你添花1 天前
Pelco KBD300A 模拟器:06+5.串口实现的逻辑优化、配置管理与协议完善(二次迭代)
python·运维开发
databook1 天前
前注意加工:让你的图表抓住读者的眼球
python·数据分析·数据可视化