🚨 实际问题
文件: DAM3000M.py - 完全无测试覆盖
测试状态: 0% 覆盖率 - 没有任何测试文件
该仓库包含 288 行硬件控制代码,但完全没有测试:
- ❌ 没有
test_*.py 文件
- ❌ 没有
tests/ 目录
- ❌ 没有
pytest.ini / pyproject.toml 测试配置
- ❌ 没有 CI/CD 测试流程
问题 1: 模块导入时加载DLL (阻断测试)
文件: DAM3000M.py:75-76
代码:
dll_path = "./DAM3000M_64.dll"
dam3000m = ctypes.WinDLL(dll_path)
问题:
- DLL 在模块导入时就加载(全局作用域)
- 硬编码的相对路径
- Windows-only 依赖无条件加载
- 导致单元测试无法运行 - 导入就会失败
问题 2: 硬件紧耦合 (无法Mock)
文件: DAM3000M.py:123-137
代码:
class DAMDevice:
handles={} # 类级别可变状态
def __init__(self, com_id: int, baud_rate: int, device_id:int):
self.handle=self._get_handle()
def _get_handle(self):
if self.com_id not in self.handles:
handle=DAM3000M_CreateDevice(self.com_id) # 直接硬件调用
assert handle not in (-1,None, 0)
assert DAM3000M_InitDevice(handle, ...)
问题:
- 没有 DLL 函数的抽象层
- 无法在测试中模拟硬件响应
- 使用断言进行错误处理(不利于测试)
- 类级别
handles 字典导致测试隔离问题
问题 3: exit 方法签名错误
文件: DAM3000M.py:146-147
代码:
def __exit__(self):
assert DAM3000M_ReleaseDevice(self.handle),"Failed to release device."
问题:
__exit__ 需要 4 个参数: (self, exc_type, exc_val, exc_tb)
- 当前实现破坏了上下文管理器协议
- 无法使用
with 语句
问题 4: 死代码
文件: DAM3000M.py:224-254
代码:
# DAM3151_device=DAM3151(4,3,5)
# data=DAM3151_device.measure_all_channels_mA_V()
# print(data)
# class ChannelController:
# DAM3060V_device_id_list=[1,2,3,4]
# ...
问题: 大量注释掉的代码块降低可维护性
🔧 改进建议
第一步: 重构使代码可测试
# 将DLL加载改为延迟加载
class DllInterface:
_instance = None
@classmethod
def get_instance(cls, dll_path: str = "./DAM3000M_64.dll"):
if cls._instance is None:
cls._instance = ctypes.WinDLL(dll_path)
return cls._instance
@classmethod
def set_mock(cls, mock_instance):
"""用于测试时注入mock"""
cls._instance = mock_instance
# 修复__exit__签名
class DAMDevice:
def __exit__(self, exc_type, exc_val, exc_tb):
DAM3000M_ReleaseDevice(self.handle)
return False # 不抑制异常
第二步: 添加测试文件 tests/test_dam3000m.py
import pytest
from unittest.mock import Mock, patch, MagicMock
import sys
sys.path.insert(0, '.')
class TestDAM3060V:
"""测试DAM3060V的模拟输出功能"""
def test_range_modes_defined(self):
"""测试量程模式定义正确"""
from DAM3000M import DAM3060V
assert 9 in DAM3060V.RangeModes # -10V ~ 10V
assert 8 in DAM3060V.RangeModes # -5V ~ 5V
assert 14 in DAM3060V.RangeModes # 0V ~ 10V
assert 13 in DAM3060V.RangeModes # 0V ~ 5V
def test_voltage_to_lsb_calculation(self):
"""测试电压到LSB的转换计算"""
from DAM3000M import DAM3060V
# mode 8: -5V ~ 5V
range_bottom, range_top = -5, 5
value = 0 # 中点
dal_lsb = ceil((value - range_bottom) * 0xFFF / (range_top - range_bottom))
assert dal_lsb == 0x7FF # 约等于一半
class TestDAM3151:
"""测试DAM3151的测量功能"""
def test_data_converter(self):
"""测试原始数据转换为mA/V"""
from DAM3000M import DAM3151
device = object.__new__(DAM3151) # 不调用__init__
device.fLsbType = 65535.0
# 测试0-20mA量程 (mode 11)
raw_data = 32767 # 中点
result = device._data_converter(raw_data, 20, 0)
assert abs(result - 10.0) < 0.1 # 约等于10mA
def test_channel_enable_mask(self):
"""测试通道使能掩码计算"""
from DAM3000M import DAM3151
assert (1 << 32) - 1 == 0xFFFFFFFF # 所有32通道使能
class TestDeviceInfo:
"""测试设备信息结构"""
def test_device_info_structure(self):
"""测试DeviceInfo结构体定义"""
from DAM3000M import DeviceInfo
info = DeviceInfo()
# 验证所有字段存在
assert hasattr(info, 'DeviceType')
assert hasattr(info, 'TypeSuffix')
assert hasattr(info, 'ModusType')
第三步: 添加测试配置 pyproject.toml
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "art-control-python-interface"
version = "0.1.0"
description = "阿尔泰科技DAM3000M系列控制代码Python接口"
dependencies = []
[project.optional-dependencies]
test = [
"pytest>=7.0",
"pytest-cov>=4.0",
"pytest-mock>=3.0"
]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--cov=DAM3000M",
"--cov-report=html",
"--cov-report=term-missing",
"--cov-fail-under=50"
]
[tool.coverage.run]
source = ["DAM3000M.py"]
omit = ["tests/*"]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
]
第四步: 添加GitHub Actions CI .github/workflows/test.yml
name: Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
runs-on: windows-latest # 需要Windows环境
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install -e ".[test]"
- name: Run tests with coverage
run: |
pytest
- name: Upload coverage report
uses: actions/upload-artifact@v3
with:
name: coverage-report
path: htmlcov/
📊 为什么重要
| 风险项 |
影响 |
| 无回归保护 |
任何代码修改都可能破坏硬件控制功能 |
| 无法安全重构 |
改进代码结构时无法验证正确性 |
| 硬件依赖测试 |
每次测试都需要实际硬件连接 |
| CI/CD阻塞 |
无法建立自动化发布流程 |
| 协作困难 |
其他开发者无法验证其修改 |
🎯 优先级
建议行动:
- 立即添加基础测试框架配置
- 重构代码使硬件依赖可注入/可mock
- 为核心计算逻辑(如
_data_converter, set_analog_output)添加单元测试
- 建立CI/CD自动化测试流程
📈 目标指标
🚨 实际问题
文件:
DAM3000M.py- 完全无测试覆盖测试状态: 0% 覆盖率 - 没有任何测试文件
该仓库包含 288 行硬件控制代码,但完全没有测试:
test_*.py文件tests/目录pytest.ini/pyproject.toml测试配置问题 1: 模块导入时加载DLL (阻断测试)
文件:
DAM3000M.py:75-76代码:
问题:
问题 2: 硬件紧耦合 (无法Mock)
文件:
DAM3000M.py:123-137代码:
问题:
handles字典导致测试隔离问题问题 3: exit 方法签名错误
文件:
DAM3000M.py:146-147代码:
问题:
__exit__需要 4 个参数:(self, exc_type, exc_val, exc_tb)with语句问题 4: 死代码
文件:
DAM3000M.py:224-254代码:
问题: 大量注释掉的代码块降低可维护性
🔧 改进建议
第一步: 重构使代码可测试
第二步: 添加测试文件
tests/test_dam3000m.py第三步: 添加测试配置
pyproject.toml第四步: 添加GitHub Actions CI
.github/workflows/test.yml📊 为什么重要
🎯 优先级
建议行动:
_data_converter,set_analog_output)添加单元测试📈 目标指标