如何在Python单元测试中Mock整个neopixel依赖文件?
解决CircuitPython neopixel模块的单元测试Mock问题
关键要在导入你的业务代码之前,就把neopixel整个模块换成Mock对象。这样当你的代码执行import neopixel时,只会加载Mock版本,根本不会去碰真实模块里那些依赖微控制器的代码。
方法一:用unittest.mock.patch提前拦截模块导入
在测试文件最开头(务必在导入你的业务模块之前),用patch把neopixel整个模块Mock掉,再配置好需要的类和方法。
示例代码(上下文管理器形式):
from unittest.mock import patch, MagicMock # 先patch,再导入业务模块 with patch('neopixel') as mock_neopixel: # 给Mock模块配置NeoPixel类 mock_neopixel.NeoPixel = MagicMock() # 此时导入业务模块,neopixel会是Mock版本 from your_module import Lane def test_lane_initialization(): lane = Lane(pin='D1', total_pixels=10) # 验证NeoPixel是否按预期被调用 mock_neopixel.NeoPixel.assert_called_once_with( 'D1', 10, brightness=0.3, auto_write=False )
也可以用装饰器形式,适合测试类:
from unittest.mock import patch, MagicMock import unittest @patch('neopixel') class TestLane(unittest.TestCase): def setUp(self): # 必须在patch生效后导入业务模块 from your_module import Lane self.Lane = Lane def test_initialization(self, mock_neopixel): mock_neopixel.NeoPixel = MagicMock() lane = self.Lane(pin='D2', total_pixels=5) mock_neopixel.NeoPixel.assert_called_once_with( 'D2', 5, brightness=0.3, auto_write=False )
方法二:直接修改sys.modules注入Mock模块
在测试文件最开头,手动把neopixel(以及其他可能报错的依赖,比如board、rainbowio)添加到sys.modules中,用Mock对象代替,Python会直接使用这些Mock,不会去查找真实模块。
示例代码:
import sys from unittest.mock import MagicMock # 创建Mock的neopixel模块 mock_neopixel = MagicMock() mock_neopixel.NeoPixel = MagicMock() # 注入到系统模块列表 sys.modules['neopixel'] = mock_neopixel # 如果board、rainbowio也会报错,同样Mock sys.modules['board'] = MagicMock() sys.modules['rainbowio'] = MagicMock() # 现在可以安全导入你的业务模块了 from your_module import Lane def test_lane_setup(): lane = Lane(pin='D3', total_pixels=8) mock_neopixel.NeoPixel.assert_called_once_with( 'D3', 8, brightness=0.3, auto_write=False )
为什么之前的patch没用?
你之前只patch了neopixel.NeoPixel类,但这个操作是在导入业务模块之后做的——而业务模块导入时,已经触发了真实neopixel模块的导入,此时它的顶层依赖(比如针对微控制器的底层库)已经开始加载并报错了。必须在业务模块导入前就完成整个模块的Mock。
内容的提问来源于stack exchange,提问作者Chris Schmitz
相关产品推荐
相关产品推荐

