第16篇. 项目硬件相关测试的 Mock 技巧详解
本项目是一个Pelco KBD300A 键盘模拟器 + Pelco-D/Pelco-P 协议维护工具,核心硬件交互集中在:
- 串口通信(
core/serial/模块):真实场景下使用pySerial操作 COM 端口。 - Pelco 协议命令/响应(
core/protocol/、core/pelco_protocol.py):发送控制命令,接收设备反馈(位置、报警、状态查询等)。 - 设备状态变化:云台、镜头、预置位、轨迹、辅助开关、报警等。
由于这些功能依赖真实硬件(球机、矩阵、键盘),在单元测试、CI/CD 或开发机上无法直接连接硬件,因此必须使用Mock技术隔离硬件依赖。
项目本身已经内置了非常完善的模拟设施(core/simulator/virtual_device.py),这大大降低了 Mock 难度。下面按场景详细介绍推荐的 Mock 技巧。
1. 最推荐方式:直接使用内置 VirtualDevice(零侵入式 Mock)
项目核心已经实现了完整的虚拟摄像机状态机VirtualDevice,它具备以下特性:
- 维护完整的设备状态(pan/tilt/zoom/focus/iris/aux/alarm 等)。
process_command(data: bytes) -> Optional[bytes]:接收 Pelco 命令字节,返回模拟响应字节(完全符合真实设备行为)。- 支持查询响应(Sense 请求)、报警触发、位置反馈等。
- 完全独立于真实串口,可在纯内存中运行。
使用方式(推荐在测试中直接注入):
fromcore.simulator.virtual_deviceimportVirtualDevicefromunittest.mockimportMagicMock,patchclassTestPTZControl:defsetup_method(self):# 创建虚拟设备,模拟摄像机 ID=1self.device=VirtualDevice(cam_id=1)# Mock SerialManager 的 write 方法,让它把数据转发给 VirtualDeviceself.mock_serial=MagicMock()deffake_write(data:bytes):response=self.device.process_command(data)ifresponse:# 模拟收到响应(触发 parsed_received 等信号)self.mock_serial.parsed_received.emit({"raw":response,"parsed":"..."})self.mock_serial.write.side_effect=fake_write# 或者直接替换 SerialManager 中的 serial_mgr 为 mockself.serial_mgr=SerialManager(...)# 正常创建self.serial_mgr._worker._ser=self.mock_serial# 替换底层 serialdeftest_ptz_move(self):fromcore.protocolimportptz_control# 发送 PTZ 移动命令ptz_control(self.serial_mgr,cam_id=1,pan_speed=50,tilt_speed=30)# 断言设备状态已变化assertabs(self.device.pan-50*0.1)<0.01# 模拟速度积分(项目内部有时间步进)assertabs(self.device.tilt-30*0.1)<0.01# 断言产生了响应(如果有查询)assertself.mock_serial.write.called优点:
- 完全模拟真实设备行为(包括报警、位置反馈、查询响应)。
- 无需额外库,项目原生支持。
- 可用于宏执行测试、协议测试、报警联动测试。
2. Mock pySerial(适用于测试 SerialWorker / SerialManager)
当需要测试串口读写线程、缓冲区管理、帧提取逻辑时,可以完全 Mockserial.Serial。
fromunittest.mockimportpatch,MagicMockimportserial@patch('serial.Serial')deftest_serial_worker_read(mock_serial_class):# Mock 串口实例mock_ser=MagicMock()mock_ser.is_open=Truemock_ser.in_waiting=10mock_ser.read.return_value=bytes([0xFF,0x01,0x00,0x07,0x20,0x40,0x68])# 一个完整的 Pelco-D 命令mock_serial_class.return_value=mock_ser# 创建 SerialWorker(会使用 mock 的 Serial)worker=SerialWorker(port="COM1",baud=9600,...)# 触发一次读取worker._read_data()# 断言解析信号被发射worker.parsed_received.emit.assert_called()# 或检查日志关键点:
- 项目在
worker.py中有try: import serial except: serial = None,所以在无 serial 环境下也能运行测试。 - 使用
patch('core.serial.worker.serial.Serial')精确控制导入位置。 - 可模拟超时、断开、乱序数据等异常场景。
3. Mock 协议工厂与协议实例(测试协议切换)
项目使用工厂模式get_protocol(serial_mgr, protocol_type)返回 PelcoD/PelcoP 实例。
@patch('core.protocol.__init__.get_protocol')deftest_protocol_switch(mock_get_protocol):mock_protocol=MagicMock()mock_get_protocol.return_value=mock_protocol# 执行切换协议操作serial_mgr.protocol="P"# 断言调用了正确的协议实例mock_get_protocol.assert_called_with(serial_mgr,"P")# 测试命令发送serial_mgr.ptz_move(1,50,30)mock_protocol.ptz_control.assert_called()4. 集成测试:Mock 整个 SerialManager 用于宏/模板执行
宏执行引擎MacroEngine依赖SerialManager。测试宏时可注入 Mock 管理器:
deftest_macro_with_virtual_device():device=VirtualDevice(cam_id=1)mock_mgr=MagicMock()defwrite_cmd(data):resp=device.process_command(data)ifresp:mock_mgr.data_received.emit(resp)# 模拟收到响应mock_mgr.write.side_effect=write_cmd# 创建宏引擎engine=MacroEngine(mock_mgr,cam_id=1)script=""" call_preset(1, 10) delay(100) ptz_move(1, 50, 30) delay(500) ptz_stop(1) """engine.set_and_run(script)# 断言预置位被调用、PTZ 状态变化assertdevice.last_preset==10assertdevice.pan>05. 其他高级技巧
- 时间加速:VirtualDevice 的状态更新依赖
time步进,可 patchtime.sleep为瞬间返回,加速长延时宏测试。 - 报警测试:调用
device.simulate_alarm(0x01)触发报警,验证execute_alarm_action被调用。 - 响应注入:在测试接收解析时,直接调用
worker._process_buffer(bytes_data)注入自定义帧。 - pytest fixture:建议封装一个 fixture 返回预配置的
VirtualDevice + Mock SerialManager,所有测试复用。
总结推荐策略
| 测试目标 | 推荐 Mock 方式 | 理由 |
|---|---|---|
| 协议命令/响应 | VirtualDevice.process_command | 最真实、最完整 |
| 宏/模板执行 | 注入 Mock SerialManager + VirtualDevice | 覆盖完整执行路径 |
| 串口读写线程、缓冲区 | patch serial.Serial | 隔离底层 IO |
| 协议切换、工厂逻辑 | patch get_protocol | 验证切换逻辑 |
| 报警联动、查询响应 | VirtualDevice.simulate_alarm / query | 直接触发状态变化 |
项目对硬件依赖的解耦做得非常好(VirtualDevice + 工厂模式),几乎所有硬件相关功能都可以通过内置模拟器进行无硬件的高保真测试。这是本项目测试友好性的最大亮点。
上一篇总目录下一篇