当前位置:首页 > 物联网 > 智能应用
[导读]嵌入式设备的测试长期依赖人工操作——烧录固件、观察串口输出、比对预期结果,重复性极高且容易遗漏边界情况。用Python搭建一套自动化测试框架,可以将这些重复劳动标准化,实现批量执行、结果断言和HTML报告生成。本文展示一个轻量级框架的完整实现,适用于串口通信的嵌入式产品(如DTU、传感器节点、工控板)。


嵌入式设备的测试长期依赖人工操作——烧录固件、观察串口输出、比对预期结果,重复性极高且容易遗漏边界情况。用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>

       <h1>嵌入式设备自动化测试报告</h1>

       <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让嵌入式测试真正步入自动化时代。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭