用Python实现嵌入式设备自动化测试框架:批量执行用例并生成报告
嵌入式设备的测试长期依赖人工操作——烧录固件、观察串口输出、比对预期结果,重复性极高且容易遗漏边界情况。用Python搭建一套自动化测试框架,可以将这些重复劳动标准化,实现批量执行、结果断言和HTML报告生成。本文展示一个轻量级框架的完整实现,适用于串口通信的嵌入式产品(如DTU、传感器节点、工控板)。
框架整体设计
核心思路:将每个测试用例封装为一个独立的Python类,通过测试套件组织批量执行,最后汇总结果并渲染报告。框架分为三层:
设备层:封装串口操作、SSH登录、Telnet等通信方式
用例层:继承基类,实现setup、test_xxx、teardown方法
报告层:收集测试结果,生成带饼图和详细日志的HTML报告
设备层封装
以串口为例,封装一个SerialDevice类,屏蔽底层细节:
import serial, time
class SerialDevice:
def __init__(self, port, baud=115200, timeout=5):
self.ser = serial.Serial(port, baud, timeout=timeout)
def exec_cmd(self, cmd, wait=0.5):
self.ser.write(f"{cmd}\n".encode())
time.sleep(wait)
return self.ser.read_all().decode()
def expect(self, pattern, timeout=5):
"""等待预期输出"""
end_time = time.time() + timeout
buf = ''
while time.time() < end_time:
buf += self.ser.read_all().decode()
if pattern in buf:
return True
return False
def close(self):
self.ser.close()
用例基类与断言
定义TestCaseBase,每个用例需实现run()方法,返回布尔值和日志:
class TestCaseBase:
def __init__(self, name, device):
self.name = name
self.device = device
self.log = []
def setup(self):
pass
def teardown(self):
pass
def assert_true(self, condition, msg):
if not condition:
raise AssertionError(msg)
self.log.append(f"[PASS] {msg}")
def run(self):
try:
self.setup()
self.test_body()
result = True
except AssertionError as e:
self.log.append(f"[FAIL] {e}")
result = False
finally:
self.teardown()
return result, '\n'.join(self.log)
具体用例示例
假设要测试设备的上电启动和AT指令响应:
class PowerOnTest(TestCaseBase):
def test_body(self):
output = self.device.exec_cmd("AT", wait=1)
self.assert_true("OK" in output, "AT指令响应正常")
class NetworkTest(TestCaseBase):
def test_body(self):
output = self.device.exec_cmd("AT+CSQ")
self.assert_true("+CSQ:" in output, "信号质量查询成功")
测试套件与批量执行
TestRunner负责收集用例、顺序执行、汇总结果:
class TestRunner:
def __init__(self, device):
self.device = device
self.results = []
def add_case(self, case_class):
case = case_class(case_class.__name__, self.device)
passed, log = case.run()
self.results.append({
'name': case.name,
'passed': passed,
'log': log
})
def run_all(self):
for r in self.results:
status = "✅ PASS" if r['passed'] else "❌ FAIL"
print(f"{status} - {r['name']}")
HTML报告生成
使用jinja2模板渲染美观的报告,包含统计图表(可用Chart.js CDN):
from jinja2 import Template
def generate_report(results, filename='report.html'):
total = len(results)
passed = sum(1 for r in results if r['passed'])
failed = total - passed
template_str = """
<html><head><script src="https://cdn.jsdelivr.net/npm/chart.js"></script></head>
<body>
<canvas id="chart" width="400" height="200"></canvas>
<script>
new Chart(document.getElementById('chart'), {
type: 'pie',
data: { labels: ['通过','失败'], datasets: [{data: [{{passed}}, {{failed}}]}] }
});
</script>
<table border="1">
{% for r in results %}
<tr style="background: {{ '#d4edda' if r.passed else '#f8d7da' }}">
<td>{{ r.name }}</td><td>{{ 'PASS' if r.passed else 'FAIL' }}</td>
<td><pre>{{ r.log }}</pre></td>
</tr>
{% endfor %}
</table>
</body></html>
"""
html = Template(template_str).render(results=results, passed=passed, failed=failed)
with open(filename, 'w') as f:
f.write(html)
print(f"报告已生成:{filename}")
主程序入口
if __name__ == '__main__':
dev = SerialDevice('/dev/ttyUSB0', 115200)
runner = TestRunner(dev)
runner.add_case(PowerOnTest)
runner.add_case(NetworkTest)
runner.run_all()
generate_report(runner.results)
dev.close()
扩展建议
参数化测试:通过装饰器传入不同波特率、固件版本等参数,自动生成多条用例。
远程设备:将SerialDevice替换为SSHDevice或TelnetDevice,统一接口即可。
持续集成:将框架嵌入Jenkins/GitLab CI,每次提交自动烧录并执行回归测试。
截图与波形:对于LCD显示类设备,可集成OpenCV截屏比对;对于模拟信号,可挂载示波器数据。
写在最后
这套框架的核心价值不在于代码量,而在于可复用的测试资产。当产品迭代时,只需新增用例类,已有的用例自动回归。配合HTML报告,研发和测试团队能一眼看出本次改动破坏了哪些功能。对于嵌入式团队来说,花两天时间搭建这样一个框架,节省的是未来数月的人工测试工时。从“手动点灯”到“一键回归”,Python让嵌入式测试真正步入自动化时代。





