Python中Gatttool命令相关单元测试与蓝牙连接上层函数测试方法问询
Python中Gatttool命令相关单元测试与蓝牙连接上层函数测试方法问询
我太懂这种困扰了——写了调用终端命令和硬件交互的代码,想做单元测试又怕每次都要连真实设备,而且还没法隔离代码逻辑和硬件问题。下面我结合pexpect和Python单元测试的常用技巧,给你拆解两种场景的测试方案,顺便先修正你代码里的一个小问题:
你的原代码里send函数直接访问gatt变量会触发NameError,因为connect里的gatt是局部变量,外部拿不到。先把代码调整成可正常运行的版本:
import pexpect MAC = "00:80:41:AE:fD:7C" def connect(): gatt = pexpect.spawn("sudo gatttool -I") gatt.sendline("connect " + MAC) gatt.expect("Connection successful") return gatt # 返回gatt实例,让上层函数能调用 def send(x): gatt = connect() gatt.sendline(f"char-write-req 0x000c {x}0") gatt.expect("Characteristic value was written successfully") send("ff")
一、单元测试:隔离硬件,验证命令调用逻辑
单元测试的核心是只测你的代码逻辑,不依赖真实硬件,这里我们用Python标准库的unittest.mock(或者pytest的pytest-mock插件)来模拟pexpect的行为,完全跳过真实的gatttool调用和蓝牙连接。
比如测试connect函数,我们要验证:
- 代码是否正确启动了
gatttool -I进程 - 是否发送了正确的连接命令
- 是否正确等待了连接成功的响应
测试代码示例(用unittest):
import unittest from unittest import mock from your_script import connect, MAC # 替换your_script为你的脚本文件名 class TestGattFunctions(unittest.TestCase): @mock.patch('pexpect.spawn') def test_connect_correctly_calls_gatttool(self, mock_spawn): # 模拟gatt实例的行为:expect能匹配到"Connection successful" mock_gatt_instance = mock_spawn.return_value mock_gatt_instance.expect.return_value = 0 # 调用要测试的connect函数 connect() # 验证调用是否符合预期 mock_spawn.assert_called_once_with("sudo gatttool -I") mock_gatt_instance.sendline.assert_called_once_with(f"connect {MAC}") mock_gatt_instance.expect.assert_called_once_with("Connection successful")
再比如测试send函数,我们可以模拟connect返回一个假的gatt实例,验证send是否正确发送了写特征值的命令:
@mock.patch('your_script.connect') # 模拟connect函数 def test_send_correctly_writes_characteristic(self, mock_connect): # 创建模拟的gatt实例 mock_gatt = mock.Mock() mock_connect.return_value = mock_gatt mock_gatt.expect.return_value = 0 # 调用send函数 send("ff") # 验证调用逻辑 mock_connect.assert_called_once() mock_gatt.sendline.assert_called_once_with("char-write-req 0x000c ff0") mock_gatt.expect.assert_called_once_with("Characteristic value was written successfully")
这种测试的好处是:
- 不需要真实蓝牙设备,跑测试快得很
- 能精准定位你的代码逻辑问题,比如参数传错了、命令拼错了
二、集成测试:验证真实硬件交互
如果要确保你的代码能和真实蓝牙设备正常工作,那就要做集成测试——这时候需要测试环境里有可用的目标蓝牙设备,而且MAC地址正确。
用pytest的话,可以给这类测试加个标记,区分开单元测试,只在有硬件的环境运行:
import pytest from your_script import send @pytest.mark.integration def test_send_to_real_device(): # 这里假设测试环境已经配对好目标蓝牙设备 send("ff") # 如果设备有反馈,还可以加额外验证,比如: # gatt = connect() # gatt.expect("Expected feedback from device") # 替换成设备实际的响应
运行的时候,只跑集成测试:pytest -m integration,平时跑单元测试就跳过这些。
三、几个小提醒
- 如果你用
sudo调用gatttool,测试时可能需要权限,要么给测试进程加sudo,要么配置gatttool不需要sudo(修改udev规则) - 模拟的时候,别忘了处理异常场景:比如模拟
expect匹配失败(返回-1),测试你的代码是否能正确处理连接失败的情况 - 尽量不要用全局变量存gatt实例,容易导致多调用时的状态混乱,最好每次调用connect都返回新实例,或者用类来管理连接状态
内容来源于stack exchange
相关产品推荐
相关产品推荐

