You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对使用python-can模块的代码进行单元测试,规避非目标环境CAN总线初始化报错

问题原因
  • 你当前的patch不生效核心原因是:UsingCanBUS类的静态属性_bus会在模块导入阶段直接执行can.ThreadSafeBus初始化,而你现有测试的patch时机晚于模块导入,真实的CAN接口已经被调用抛出异常,mock没有来得及替换目标对象。
  • 另外如果测试运行时mymodule已经被提前导入过,后续的patch也无法覆盖已经加载的模块内容。
解决方案1:调整patch时机(无需修改业务代码)

在测试会话启动、任何业务代码导入之前,就完成对mymodule.can的全局mock,使用pytest的自动fixture实现最方便:

  1. 在tests目录下新建conftest.py,写入全局mock逻辑:
# tests/conftest.py
import pytest
from unittest.mock import MagicMock, patch

@pytest.fixture(scope="session", autouse=True)
def mock_can_module():
    # 创建mock的can模块对象
    mock_can = MagicMock()
    # 预先设置需要用到的属性和返回值
    mock_can.CanError = type("CanError", (Exception,), {})
    mock_bus = MagicMock()
    mock_can.ThreadSafeBus.return_value = mock_bus
    # 全局替换mymodule中导入的can模块,整个测试周期生效
    with patch("mymodule.can", mock_can):
        yield mock_can, mock_bus
  1. 测试函数直接导入业务代码即可,此时所有CAN相关调用都会走mock对象:
#./tests/test_utils/test_utils.py
def test_something(mock_can_module):
    mock_can, mock_bus = mock_can_module
    from mymodule.utils import some_function
    # 可自定义mock的返回值/异常,验证逻辑正确性
    mock_bus.send.side_effect = mock_can.CanError
    
    assert some_function() == expected_result
解决方案2:优化业务代码(更推荐,长期可维护性更高)

把静态总线的初始化改为懒加载,仅在第一次实例化类的时候才创建总线对象,避免导入阶段就调用硬件接口:

# ./mymodule/__init__.py
import can
from typing import Optional

class UsingCanBUS:
    _bus: Optional[can.ThreadSafeBus] = None
    
    def __new__(cls, *args, **kwargs):
        # 第一次创建实例时才初始化总线
        if cls._bus is None:
            cls._bus = can.ThreadSafeBus(channel='can0', bustype='socketcan')
        return super().__new__(cls, *args, **kwargs)
    
    def __init__(self) -> None:
        # 原有逻辑不变
        self._listener = can.Listener()
        self._listener.on_message_received = self._handle_message

    def _send_message(self, id, data) -> bool:
        msg = can.Message(arbitration_id=id, data=data, extended_id=False)
        try:
            self._bus.send(msg)
        except can.CanError:
            return False
        else:
            return True

改完之后测试时不需要处理导入时机问题,只需要在创建UsingCanBUS实例之前完成patch即可,也方便后续生产环境动态修改总线参数。

内容的提问来源于stack exchange,提问作者powerbling

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 09:39:00