4. 自动化框架设计:模块化设计思想、配置文件管理、日志系统搭建

做光模块测试自动化,框架设计是地基。地基没打好,后面写再多脚本也是白搭。我见过太多团队,一开始图省事,把所有代码塞到一个文件里。结果呢?换一个测试项目,整个脚本就得重写。今天咱们聊聊怎么搭一个靠谱的框架。

4.1 模块化设计思想

说白了,模块化就是把大问题拆成小问题。每个模块只干一件事,干好一件事。

我个人的习惯是把测试框架分成三层:

  • 驱动层:跟仪器仪表打交道。比如控制光功率计、误码仪、光谱仪。
  • 业务层:封装测试流程。比如“测发射功率”、“测灵敏度”。
  • 执行层:调度测试用例,生成报告。

为什么要这么分?你想想看,如果驱动层和业务层混在一起,换一台不同品牌的误码仪,你就要改所有测试用例。分开之后,只需要改驱动层的一个文件就行。

核心原则:高内聚,低耦合

每个模块内部要足够完整,模块之间依赖要尽量少。

我在项目中遇到过这样一个坑:有个同事把仪器初始化代码写在了每个测试用例里。结果仪器换了IP地址,他改了30多个文件。嗯,从那以后我强制要求:所有仪器控制代码,只准放在驱动层。

4.2 配置文件管理(YAML/JSON)

测试参数不能硬编码在代码里。为什么?因为产线换一个型号的光模块,参数就全变了。你总不能每次都改代码吧?

我推荐用YAML。JSON虽然也行,但YAML可读性更好,支持注释,写起来也省事。

来看一个典型的配置文件:

# config.yaml
device:
  power_meter:
    type: "keysight_8163B"
    ip: "192.168.1.100"
    port: 5025
  
  oscilloscope:
    type: "tektronix_MSO54"
    ip: "192.168.1.101"

test_params:
  tx_power:
    wavelength: 1310
    min: -3.0
    max: 2.0
    unit: "dBm"
  
  sensitivity:
    bit_rate: 25.78125
    ber_threshold: 1e-12
    prbs_pattern: "PRBS31"

report:
  format: "html"
  output_dir: "./reports"
  include_charts: true

读取配置的代码也很简单:

import yaml

def load_config(path="config.yaml"):
    with open(path, 'r') as f:
        config = yaml.safe_load(f)
    return config

# 使用
cfg = load_config()
pm_ip = cfg['device']['power_meter']['ip']
tx_min = cfg['test_params']['tx_power']['min']

小技巧:配置文件中可以加一个 version 字段。这样当配置格式变化时,脚本可以自动判断是否需要迁移。我曾经因为配置格式改了没通知,导致产线停了半小时...

4.3 日志系统搭建

日志这东西,平时觉得没用,出问题的时候才知道有多重要。产线上的测试,一跑就是几千个模块。哪个模块在哪一步失败了?为什么失败?没有日志,你只能靠猜。

我建议用Python的logging模块,别自己造轮子。它支持分级、支持输出到文件和终端、支持按大小自动轮转。

一个成熟的日志配置:

import logging
import logging.handlers
import os

def setup_logger(name="test_automation", log_dir="./logs"):
    os.makedirs(log_dir, exist_ok=True)
    
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)
    
    # 文件日志 - 记录所有信息
    file_handler = logging.handlers.RotatingFileHandler(
        os.path.join(log_dir, "test.log"),
        maxBytes=10*1024*1024,  # 10MB
        backupCount=5
    )
    file_handler.setLevel(logging.DEBUG)
    
    # 控制台日志 - 只显示INFO及以上
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    
    # 格式化
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    )
    file_handler.setFormatter(formatter)
    console_handler.setFormatter(formatter)
    
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    
    return logger

# 使用
log = setup_logger()
log.info("开始测试模块 S/N: 2024A0001")
log.debug("设置光功率计波长: 1310nm")
log.error("误码率超标: 2.3e-9 > 1e-12")

注意:日志级别要合理使用。DEBUG用于开发调试,INFO记录正常流程,WARNING提示潜在问题,ERROR记录失败。别把所有信息都打成INFO,否则日志文件会膨胀得很快。

日志里还要记录关键信息:模块序列号、测试时间、测试版本、仪器状态。这样出了问题才能复现。我曾经排查一个间歇性故障,就是靠日志里记录的仪器返回码,发现是GPIB线接触不良。

4.4 整体框架结构

把上面三块合在一起,一个典型的项目结构是这样的:

test_automation/
├── config/
│   ├── config.yaml          # 主配置
│   └── device_map.yaml      # 仪器映射
├── drivers/
│   ├── power_meter.py       # 光功率计驱动
│   ├── ber_tester.py        # 误码仪驱动
│   └── osa.py               # 光谱仪驱动
├── tests/
│   ├── tx_power_test.py     # 发射功率测试
│   ├── sensitivity_test.py  # 灵敏度测试
│   └── eye_mask_test.py     # 眼图模板测试
├── utils/
│   ├── logger.py            # 日志工具
│   └── report.py            # 报告生成
├── main.py                  # 主入口
└── requirements.txt         # 依赖清单

这个结构我用了好几年,从10G到400G,从单模到多模,基本没大改过。每次新项目,改改配置文件,加几个测试用例,框架本身几乎不动。

一句话总结:模块化让代码好维护,配置文件让参数好管理,日志系统让问题好排查。这三样做好了,你的自动化框架就稳了。

光模块测试自动化框架架构 执行层 (main.py) 业务层 (tests/) 发射功率测试 | 灵敏度测试 | 眼图模板测试 | 光谱测试 驱动层 (drivers/) 光功率计 | 误码仪 | 光谱仪 | 示波器 | 可调光源 基础层 (config/ + utils/) YAML配置文件 | 日志系统 | 报告生成 | 数据存储 调度 流程 控制 支撑

这个图就是咱们框架的核心逻辑。从上到下,层层调用。每一层只跟相邻层打交道,不跨层调用。这样做的好处是:换仪器只改驱动层,加测试用例只改业务层,改参数只改配置文件。

嗯,框架设计就聊到这儿。记住一句话:好的框架是改出来的,不是设计出来的。先跑通一个最简单的流程,然后慢慢迭代。别一开始就想搞个大而全的东西,那样反而容易把自己绕进去。