Pelco KBD300A 模拟器:TEST02.重构后键盘部分的测试操作一步一步详细指导

TEST02.重构后键盘部分的测试操作一步一步详细指导

在上一篇《重构后键盘部分的测试方案规划》中,我们从宏观角度梳理了测试的整体思路:为什么要测、测什么、覆盖率目标以及测试分类。那篇文章更像是一份蓝图,帮助我们建立测试体系的战略方向。

本篇《重构后键盘部分的测试操作一步一步详细指导》则承接前文,聚焦于具体的落地实践。我们将从环境准备开始,逐步讲解如何编写单元测试、集成测试、端到端测试,并补充手动验证环节。目标是让读者不仅理解测试的重要性,还能亲手跑通整个流程,获得可量化的覆盖率结果。预计耗时 2--4 小时,适合在 Windows 7 环境下完整演练。

前提:项目已克隆到本地,Python 3.7 环境激活,pyserial/PyQt5 已安装。Win7 注意:用管理员运行命令提示符,避免权限问题。


步骤1:环境准备(10--15 分钟)

  1. 安装测试依赖

    • 打开命令提示符(cmd.exe),导航到项目根目录。

    • 创建/激活 virtualenv(推荐,避免全局污染):

      bash 复制代码
      python -m venv venv
      venv\Scripts\activate
    • 安装依赖(requirements.txt 已含 PyQt5/pyserial,再加测试工具):

      bash 复制代码
      pip install pytest==7.4.0 pytest-qt==4.4.0 coverage==6.5.0

      (这些版本兼容 Python 3.7 和 Win7,无高版本依赖冲突。)

  2. 创建 tests/ 目录和测试文件

    • 在项目根新建 tests/keyboard/

    • 添加示例测试文件(从规划复制,稍后详述)。初始结构如下:

      text 复制代码
      tests/
      ├── keyboard/
      │   ├── test_panel.py        # 单元:按键 / LCD
      │   ├── test_integration.py  # 集成:信号 + 串口 mock
      ├── analyze_coverage.py      # 覆盖率分析脚本
      ├── test_e2e.py              # 端到端:全链路
      └── conftest.py              # 全局 fixtures(如 QApplication)

      简短的说明表格:

      文件/目录 作用说明
      keyboard/ 键盘相关测试模块目录
      test_panel.py 单元测试:按键输入、LCD 显示
      test_integration.py 集成测试:信号与串口交互
      test_e2e.py 端到端测试:完整链路验证
      analyze_coverage.py 覆盖率分析辅助脚本
      conftest.py 全局 pytest fixtures(QApplication 等)
  3. 添加 conftest.py(全局 fixture)

    python 复制代码
    # tests/conftest.py
    # 全局 fixture 配置
    import sys
    import os
    import pytest
    from PyQt5.QtWidgets import QApplication
    from unittest.mock import MagicMock
    # 添加项目根目录到 sys.path(tests/ 的上两级)
    project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
    if project_root not in sys.path:
        sys.path.insert(0, project_root)
    
    
    @pytest.fixture(scope="session")
    def qapp():
        """
        全局 QApplication fixture,整个测试会话只初始化一次。
        """
        app = QApplication.instance() or QApplication([])
        yield app
        app.quit()
    
    
    @pytest.fixture
    def app_window(qapp):
        """
        提供一个完整的 AppWindow 实例,并在测试结束后自动关闭。
        避免在各个测试文件中重复定义。
        """
        from ui.main_window import AppWindow
        win = AppWindow()
        win.show()
        qapp.processEvents()
        yield win
        win.close()
    
    
    @pytest.fixture
    def keyboard_panel(qapp):
        """
        提供一个独立的 KeyboardPanel 实例,用于单元测试。
        """
        from ui.keyboard.panel import KeyboardPanel
        panel = KeyboardPanel()
        qapp.processEvents()
        yield panel
        panel.close()

    这样,所有测试文件都可以直接使用 qapp fixture,而无需重复初始化 QApplication。

  4. 添加 analyze_coverage.py(覆盖率分析辅助脚本)

    python 复制代码
    # tests/analyze_coverage.py
    import json
    
    def main():
        with open("coverage.json", "r", encoding="utf-8") as f:
            data = json.load(f)
        totals = data["totals"]
        print(f"整体覆盖率: {totals['percent_covered_display']}%")
        for filename, info in data["files"].items():
            pct = info["summary"]["percent_covered_display"]
            if int(pct) < 50:
                print(f"{filename}: 覆盖率 {pct}%")
                print("缺失行:", info["missing_lines"])
    
    if __name__ == "__main__":
        main()

    运行 python tests/analyze_coverage.py 可以快速定位覆盖率薄弱的文件和缺失行,指导后续补充测试。

  5. 验证环境

    • 运行 pytest --version,预期输出 pytest 7.4.0。

    • 运行项目:python app.py,检查 UI 正常(键盘显示)。

    • 检查 pytest.ini 配置:

      ini 复制代码
      markers =
          keyboard: 测试键盘 UI 与交互
      filterwarnings =
          ignore:.*sipPyTypeDict.*:DeprecationWarning

      这样运行时可以用 pytest -m keyboard 来跑所有键盘相关测试,并且不会刷屏显示 PyQt5 的弃用告警。


步骤2:单元测试(隔离组件)

目标:测试单个文件(如 panel.py 的 _on_digit),不跑 GUI/串口。快速反馈。

  1. 编写/运行 test_panel.py

    python 复制代码
    - 预期:所有 `@pytest.mark.keyboard` 的测试通过。  
       - 如果失败:检查 import 路径(from ui.keyboard.panel import KeyboardPanel)。pytest tests/keyboard/test_panel.py -v -W ignore::DeprecationWarning# tests/keyboard/test_panel.py
    import pytest
    from ui.keyboard.panel import KeyboardPanel
    from unittest.mock import MagicMock
    
    @pytest.fixture
    def keyboard(qapp):
        return KeyboardPanel()
    
    @pytest.mark.keyboard
    def test_on_digit_limit(keyboard):
        for i in range(5):  # 输入5位
            keyboard._on_digit(1)
        assert len(keyboard.input_buffer) == 4  # 限长4
        assert keyboard.lcd.display.called  # mock 检查调用
    @pytest.mark.keyboard
    def test_set_mode(keyboard):
        keyboard.mode_changed = MagicMock()
        keyboard.lcd.display_text = MagicMock()
        keyboard._set_mode("PRESET")
        assert keyboard.mode == "PRESET"
        keyboard.mode_changed.emit.assert_called_with("PRESET")
        keyboard.lcd.display_text.assert_called_with("PRESET", ...)
  2. 运行

    sh 复制代码
    pytest -m keyboard -v  
    • 预期:所有 test_ 通过(e.g., 2 passed)。
    • 如果失败:检查 import 路径(from ui.keyboard.panel import KeyboardPanel)。
  3. 类似测试 LCD/indicator

    • test_lcd.py:mock theme,检查 display_text 的 setStyleSheet。
    • test_indicator.py:mock QTimer,检查 flash 后 off 调用。
  4. 覆盖率检查

    复制代码
    coverage run -m pytest tests/keyboard/
    coverage report -m  # 显示缺失行
    coverage html       # 生成 htmlcov/index.html 查看
    • 目标:ui/keyboard/ 覆盖 90%+。

步骤3:集成测试(模块交互)

目标:测试键盘与核心/mock 串口的交互(如按键 emit → 串口 write)。

  1. 编写 test_integration.py

    python 复制代码
    # tests/keyboard/test_integration.py
    import pytest
    from unittest.mock import patch, MagicMock
    from ui.keyboard.panel import KeyboardPanel
    from core.protocol import ptz_control
    
    @pytest.fixture
    def keyboard(qapp):
        kb = KeyboardPanel()
        kb.serial_mgr = MagicMock()  # mock 串口
        return kb
    
    @pytest.mark.keyboard
    def test_joystick_to_protocol(keyboard):
        with patch('core.protocol.ptz_control') as mock_ptz:
            keyboard.joystick_moved.emit(10, -20)
        mock_ptz.assert_called_with(keyboard.serial_mgr, ..., 10, -20)
        
    @pytest.mark.keyboard
    def test_enter_error_indicator(keyboard):
        keyboard.input_buffer = "invalid"
        keyboard.indicator_manager.on = MagicMock()
        keyboard._on_enter()
        keyboard.indicator_manager.on.assert_called_with("ERR")
  2. 运行

    复制代码
    pytest -m keyboard -v
    • 预期:通过,mock assert_called 检查交互。
  3. 协议集成测试

    • 新建 test_protocol_integration.py:mock build_pelco_d,检查 checksum。

步骤4:端到端测试(全链路)

目标:模拟用户操作,验证 UI → 核心 → 反馈。

  1. 安装 pytest-qt (如果未装):pip install pytest-qt==4.0.2

  2. 编写 test_e2e.py

    python 复制代码
    # tests/test_e2e.py
    import pytest
    from PyQt5.QtTest import QTest
    from PyQt5.QtCore import Qt
    from ui.main_window import AppWindow
    
    @pytest.mark.keyboard
    def test_full_keyboard_flow(qapp):
        window = AppWindow()
        window.show()  # 显示窗口
        qapp.processEvents()
    
        # 模拟数字输入
        for digit in "123":
            btn = window.keyboard.findChild(QtWidgets.QPushButton, digit)  # 假设 text=digit
            QTest.mouseClick(btn, Qt.LeftButton)
        assert window.keyboard.lcd.value() == 123  # 检查 LCD
    
        # 模拟 ENT
        ent_btn = window.keyboard.findChild(QtWidgets.QPushButton, "ENT")
        QTest.mouseClick(ent_btn, Qt.LeftButton)
        # 检查 TX 闪(indicator_manager.state("TX") 暂 True)
    
        # 异常:输入无效后 ENT,检查 ERR on
  3. 运行

    复制代码
    pytest -m keyboard -v
    • 预期:窗口短暂闪现,通过(Win7 可能慢,加 -s 显示输出)。
    • 注意:端到端慢,标记 @pytest.mark.slow。

步骤5:手动测试与验证

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

  1. 运行项目python app.py

  2. Checklist 操作

    • 输入 1234 → LCD 显示 1234,超限不增。
    • 按 PRESET → LCD "PRESET" + 颜色变(themes.py)。
    • 摇杆拖拽 → TX 闪(模拟发送)。
    • 切换主题(右上按钮) → LCD/灯颜色变。
    • resize 窗口 → 布局不崩(stretch 生效)。
    • 模拟错误(断串口) → ERR 灯 + 弹窗。
    • Win7 特定:多分辨率(1024x768)测试;旧 COM 端口连接真实设备验证。
  3. 最终覆盖率

    复制代码
    coverage run -m pytest -m keyboard
    coverage report -m
    coverage html
    • ui/keyboard/ 覆盖率 90%+。

步骤6:常见问题与调试

  • 失败:ImportError → 检查 PYTHONPATH(sys.path.append("...") 在 test)。
  • Win7 卡顿:减 QTimer 间隔;关防病毒。
  • CI:GitHub Actions 加 Win7 runner(自建或第三方)。
  • 扩展:加 @pytest.mark.parametrize 参数化(e.g., 多模式测试)。

通过本文的详细操作,你已经能够在本地环境中完成键盘部分的测试:从单元到集成,再到端到端和手动验证,覆盖率报告也能直观呈现测试成果。与上一篇的规划篇形成呼应,本篇提供了实操指南,真正把测试方案落地。

至此,键盘模块的测试体系已经基本完善。下一篇我们将进入宏系统的开发与测试,继续扩展整个项目的可靠性与可维护性。这样,一个完整的测试闭环就逐步建立起来,为后续功能迭代打下坚实基础。

上一篇 总目录 下一篇

相关推荐
luoluoal13 小时前
基于python的旅游景点方面级别情感分析语料库与模型(源码+文档)
python·mysql·django·毕业设计·源码
directx3d_beginner13 小时前
ifcconvert转换ifc为Obj
开发语言·python
滴啦嘟啦哒13 小时前
【机械臂】【视觉】一、加入摄像机并实现世界坐标与像素坐标的互相转换
python·深度学习·vla
YJlio13 小时前
Sysinternals 学习笔记(15.0):系统信息工具总览——RAMMap、RU、CoreInfo 等一网打尽
开发语言·笔记·python·学习·django·pdf·硬件架构
Root_Smile13 小时前
【Python】pip freeze用法
开发语言·python·pip
qq_124987075313 小时前
基于Hadoop的黑龙江旅游景点推荐系统的设计与实现(源码+论文+部署+安装)
大数据·hadoop·分布式·python·信息可视化
Knight_AL14 小时前
从 QueryWrapper 到 XML:一次「报表 SQL」的重构实践
xml·sql·重构
不过如此195114 小时前
Jira系统中JQL语句的介绍
python·sql·jira
企微自动化14 小时前
企业微信二次开发实战:突破限制,实现外部群消息主动推送
自动化·企业微信
不过如此195114 小时前
Python操作Jira实现不同项目之间的Issue同步
python·jira·issue