【发布时间】:2022-10-05 13:18:59
【问题描述】:
我不确定这个问题的标题,因为用一句话来描述这个问题并不容易。如果有人可以建议一个更好的标题,我会编辑它。
考虑使用smbus2 与 I2C 设备通信的代码:
# device.py
import smbus2
def set_config(bus):
write = smbus2.i2c_msg.write(0x76, [0x00, 0x01])
read = smbus2.i2c_msg.read(0x76, 3)
bus.i2c_rdwr(write, read)
我希望在不访问 I2C 硬件的情况下对此进行单元测试,尽我所能模拟 smbus2 模块(我尝试模拟整个 smbus2 模块,因此它甚至不需要安装,但是没有成功,所以我只好在测试环境中导入smbus2,即使它实际上并没有被使用——到目前为止没什么大不了的,我稍后会处理):
# test_device.py
# Depends on pytest-mock
import device
def test_set_config(mocker):
mocker.patch('device.smbus2')
smbus = mocker.MagicMock()
device.set_config(smbus)
# assert things here...
breakpoint()
在断点处,我正在检查 pdb 中的 bus 模拟:
(Pdb) p smbus
<MagicMock id='140160756798784'>
(Pdb) p smbus.method_calls
[call.i2c_rdwr(<MagicMock name='smbus2.i2c_msg.write()' id='140160757018400'>, <MagicMock name='smbus2.i2c_msg.read()' id='140160757050688'>)]
(Pdb) p smbus.method_calls[0].args
(<MagicMock name='smbus2.i2c_msg.write()' id='140160757018400'>, <MagicMock name='smbus2.i2c_msg.read()' id='140160757050688'>)
(Pdb) p smbus.method_calls[0].args[0]
<MagicMock name='smbus2.i2c_msg.write()' id='140160757018400'>
不幸的是,此时传递给write() 和read() 的参数已经丢失。它们似乎没有记录在smbus 模拟中,我无法在数据结构中找到它们。
有趣的是,如果我在write 和read 分配之后中断set_config() 函数,并检查模拟模块, 我可以看到:
(Pdb) p smbus2.method_calls
[call.i2c_msg.write(118, [160, 0]), call.i2c_msg.read(118, 3)]
(Pdb) p smbus2.method_calls[0].args
(118, [160, 0])
因此,参数已作为method_call 存储在smbus2 模拟中,但没有复制到传递给函数的smbus 模拟中。
为什么不保留这些信息?有没有更好的方法来测试这个功能?
我认为这可以概括为:
In [1]: from unittest.mock import MagicMock
In [2]: foo = MagicMock()
In [3]: bar = MagicMock()
In [4]: w = foo.write(1, 2)
In [5]: r = foo.read(1, 2)
In [6]: bar.func(w, r)
Out[6]: <MagicMock name='mock.func()' id='140383162348976'>
In [7]: bar.method_calls
Out[7]: [call.func(<MagicMock name='mock.write()' id='140383164249232'>, <MagicMock name='mock.read()' id='140383164248848'>)]
请注意,bar.method_calls 列表包含对函数 .write 和 .read 的调用(好),但缺少传递给这些函数的参数(坏)。这似乎破坏了此类模拟的有用性,因为它们不会像我预期的那样交互。有没有更好的方法来处理这个?
【问题讨论】:
-
set_config的代码采用bus参数,但似乎没有使用它——而是直接使用smbus模块。这可能是个问题吗? -
你试过简单
mocker.patch('smbus2')吗? -
@SamuelDion-Girardeau 哎呀-不知何故,我粘贴的代码中缺少一行-抱歉!我现在会解决这个问题。缺少的行确实使用了
bus。 -
@Adrian 是的,我已经尝试过了,这会导致
read和write变量的 Mocks,这没关系,但它们不包含用于创建它们的参数,因为它们存储在smbus2.i2c_msg代替模拟。因此它们与bus模拟无关,并且测试无法以可靠的方式建立连接(smbus2模拟可以通过测试进行检查,并且参数存在于其中,但是当进行了更多i2c_msg.read/write调用,没有指向此特定调用的链接,因此它没有用)。 -
@davidA 感谢您的更新,这现在更有意义了!刚刚放弃了一个答案,希望它有帮助。
标签: unit-testing dependency-injection mocking pytest pytest-mock