4. 测试自动化框架选型:Python unittest、pytest、Robot Framework、Selenium对比

做硬件测试的兄弟,一听到「自动化框架」四个字,第一反应往往是:「那是软件测试的事吧?」

嗯,我以前也这么想。直到有一次,我负责一个智能硬件的量产项目,每天要手动跑2000次固件升级测试,手指头都快按断了。从那天起,我彻底明白了——硬件测试的自动化,不是可选项,是必选项

今天咱们就来聊聊,市面上最常用的四个Python生态测试框架:unittestpytestRobot FrameworkSelenium。注意,Selenium虽然主打Web自动化,但在硬件测试里,它经常被用来做嵌入式Web管理页面的交互测试,所以也拉进来一起对比。

4.1 先看底层逻辑:你究竟需要什么?

选框架之前,我建议你先问自己三个问题:

  1. 谁来写测试? 是纯硬件工程师,还是软硬兼修的测试开发?
  2. 被测对象是什么? 是命令行接口、串口协议、还是Web管理页面?
  3. 测试报告给谁看? 老板要看高大上的HTML报告,还是你自己看log就行?

这三个问题决定了你的选型方向。别一上来就追新框架,我见过太多团队,框架换了三四个,测试用例一个没写出来。

4.2 Python unittest:老牌正统,但有点「重」

unittest是Python标准库自带的,不用装任何第三方包。它的设计思路模仿了Java的JUnit,非常规整。

适用场景: 团队有Java背景、需要严格遵循测试规范、测试用例数量不大(几百个以内)。

举个例子,你要测试一个电源模块的电压输出:

import unittest
import serial

class TestPowerModule(unittest.TestCase):
    def setUp(self):
        # 每次测试前,打开串口连接
        self.ser = serial.Serial('COM3', 115200, timeout=1)
    
    def tearDown(self):
        self.ser.close()
    
    def test_output_voltage_12v(self):
        self.ser.write(b'MEAS:VOLT?\n')
        voltage = float(self.ser.readline().strip())
        self.assertAlmostEqual(voltage, 12.0, delta=0.1)
    
    def test_output_voltage_5v(self):
        self.ser.write(b'MEAS:VOLT:5V?\n')
        voltage = float(self.ser.readline().strip())
        self.assertAlmostEqual(voltage, 5.0, delta=0.05)

if __name__ == '__main__':
    unittest.main()

我个人习惯用unittest做底层库的单元测试,比如写一个I2C驱动库,用unittest跑一遍,心里踏实。但说实话,它的缺点也很明显:样板代码太多。每个测试类都要写setUptearDown,测试数据驱动也不够灵活。

小技巧: 如果你非要用unittest,建议配合parameterized库来做参数化测试,能省不少代码量。

4.3 pytest:我目前的主力框架

如果让我只推荐一个框架,那就是pytest。它比unittest简洁太多,而且插件生态极其丰富。

同样的电源模块测试,用pytest写出来是这样的:

import pytest
import serial

@pytest.fixture
def serial_conn():
    """串口连接夹具,自动管理资源"""
    ser = serial.Serial('COM3', 115200, timeout=1)
    yield ser
    ser.close()

def test_output_voltage_12v(serial_conn):
    serial_conn.write(b'MEAS:VOLT?\n')
    voltage = float(serial_conn.readline().strip())
    assert abs(voltage - 12.0) < 0.1

def test_output_voltage_5v(serial_conn):
    serial_conn.write(b'MEAS:VOLT:5V?\n')
    voltage = float(serial_conn.readline().strip())
    assert abs(voltage - 5.0) < 0.05

看到了吗?没有类,没有self,直接用函数。fixture机制比setUp/tearDown灵活太多了。我在项目中遇到过最头疼的问题——测试用例之间共享硬件资源,用pytest的scope="module"scope="session"轻松搞定。

pytest在硬件测试中的杀手锏:
  • 插件丰富: pytest-html生成报告,pytest-xdist并行执行,pytest-rerunfailures失败重跑
  • fixture机制: 完美管理仪器连接、串口、GPIO等硬件资源
  • 参数化: 一行代码搞定多组测试数据
  • 断言直观: 直接用Python的assert,不用记一堆assertEqual
注意: pytest的fixture如果使用不当,容易造成资源泄漏。我曾经犯过一个错——在fixture里打开了仪器连接,但忘记写yield之后的清理代码,结果跑完1000个用例,仪器连接数爆了。记住:有借有还,再借不难

4.4 Robot Framework:关键字驱动的「傻瓜相机」

Robot Framework是个另类。它不要求你写Python代码,而是用「关键字」来描述测试步骤。说白了,它把测试用例变成了「填表格」。

同样的电源测试,用Robot Framework写出来是这样的:

*** Settings ***
Library    SerialLibrary

*** Test Cases ***
测量12V输出电压
    [Setup]    Open Serial Port    COM3    115200
    Write Data    MEAS:VOLT?\n
    ${voltage}=    Read Until    \n
    Should Be Equal As Numbers    ${voltage}    12.0    delta=0.1
    [Teardown]    Close Serial Port

这个框架最大的优势是:测试用例可读性极强。不懂编程的硬件工程师、产线技术员,都能看懂甚至编写用例。我在一个代工厂的项目里用过Robot Framework,产线上的兄弟培训了半小时就能上手改测试参数了。

但它的缺点也很致命:调试困难。一旦出问题,你很难定位是关键字库的bug还是测试逻辑的问题。而且,如果你要做复杂的条件判断、循环、数据处理,Robot Framework会变得非常别扭。

我的建议: Robot Framework适合「测试用例由非开发人员编写」的场景。如果你的团队全是程序员,别用这个,直接用pytest更高效。

4.5 Selenium:硬件测试里的「跨界选手」

Selenium是Web自动化测试的标配,但为什么硬件测试也要聊它?

因为现在越来越多的嵌入式设备,都带了一个Web管理页面。路由器、智能网关、工业控制器……你想想看,这些设备的配置界面,本质上就是个Web应用。你要测试「修改WiFi密码后设备是否正常重启」,用Selenium模拟浏览器操作,比用串口发AT指令靠谱多了。

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

def test_wifi_reconnect():
    driver = webdriver.Chrome()
    driver.get("http://192.168.1.1")
    
    # 登录
    driver.find_element(By.ID, "username").send_keys("admin")
    driver.find_element(By.ID, "password").send_keys("admin123")
    driver.find_element(By.ID, "login-btn").click()
    time.sleep(2)
    
    # 修改WiFi密码
    driver.find_element(By.LINK_TEXT, "无线设置").click()
    driver.find_element(By.ID, "wifi_password").clear()
    driver.find_element(By.ID, "wifi_password").send_keys("NewPass123!")
    driver.find_element(By.ID, "save-btn").click()
    time.sleep(5)  # 等待设备重启
    
    # 验证设备是否在线
    driver.get("http://192.168.1.1")
    assert "登录" in driver.title
    
    driver.quit()
避坑指南: 我曾经用Selenium测试一个工业路由器的Web界面,发现脚本在Chrome上跑得好好的,换到Edge就各种报错。后来查了半天,是设备Web页面的CSS选择器在不同浏览器下解析不一致。所以,用Selenium测硬件Web界面,一定要固定浏览器版本,最好用Headless模式跑在服务器上。

4.6 横向对比:一张表说清楚

维度 unittest pytest Robot Framework Selenium
学习曲线 低(标准库) 低(关键字驱动)
代码量 多(样板代码) 少(但关键字库需提前封装) 中等
硬件资源管理 手动setUp/tearDown fixture(强大) 通过Library管理 不适用
报告生成 需第三方库 pytest-html(优秀) 自带HTML报告 需配合pytest
并行执行 不支持 pytest-xdist 支持(但配置复杂) 需配合pytest
适用场景 底层库单元测试 通用硬件测试(首选) 非技术人员编写用例 嵌入式Web界面测试

4.7 我的最终选型建议

说了这么多,到底怎么选?我个人的经验是:

  • 如果你是一个人单干,或者团队全是开发背景: 无脑选pytest。它是最平衡的选择,既能写单元测试,也能做集成测试,插件生态能解决你90%的需求。
  • 如果产线技术员也要写测试用例:Robot Framework做上层封装,底层用Python库实现关键字。记住,关键字库一定要由资深工程师维护,否则会变成一锅粥。
  • 如果被测设备有Web管理页面:Selenium集成到pytest框架里,用pytest管理测试流程,用Selenium操作浏览器。这是最优雅的方案。
  • 至于unittest: 除非你公司强制要求,或者项目有历史遗留代码,否则我建议你直接跳过。不是说它不好,而是pytest能做得更好。
最后说一句: 框架只是工具,别在选型上纠结太久。我见过有人花了两周时间对比框架,结果用例一行没写。先跑起来,再优化,这才是量产测试的务实之道。