车载测试框架:CarTestManager、模拟器测试、硬件在环测试、自动化测试脚本

做车载系统开发,最怕什么?

我最怕的是——代码写完了,但不知道它到底能不能跑。尤其是Android Automotive OS这种牵一发动全身的系统,一个Service挂掉,整个IVI都可能黑屏。所以今天我们来聊聊车载测试框架,说白了就是怎么保证你的代码在车上不出事。

测试框架的核心:CarTestManager

CarTestManager是Android Automotive OS提供的一套测试基础设施。它不是某个具体的测试工具,而是一个管理测试生命周期的框架。我刚开始接触它的时候,觉得它跟普通的Instrumentation测试差不多,后来才发现,它解决了一个关键问题——车载环境下的权限和依赖管理。

举个例子,你要测试一个空调控制功能。普通测试只需要调API就行,但在车上,你得确保CarService已经启动、HAL层已经就绪、甚至某些硬件状态是正常的。CarTestManager就是帮你搞定这些前置条件的。

核心能力:

  • 管理CarService的启动与关闭
  • 模拟车辆属性(Vehicle Property)的读写
  • 控制HAL层的回调行为
  • 提供测试专用的权限集

写测试用例时,我习惯这样用:

@RunWith(CarTestManagerJUnit4ClassRunner.class)
public class HVACControllerTest {
    @Rule
    public CarTestManager mCarTestManager = new CarTestManager();

    @Test
    public void testSetTemperature() throws Exception {
        Car car = mCarTestManager.createCar();
        HVACManager hvac = (HVACManager) car.getCarManager(Car.HVAC_SERVICE);
        
        // 设置目标温度
        hvac.setTemperature(25.0f);
        
        // 验证属性值
        float actual = hvac.getTemperature();
        assertEquals(25.0f, actual, 0.1f);
    }
}

这里有个坑,我曾经踩过——createCar()方法默认不会启动所有Service。如果你测试的功能依赖某个特定Service,记得用@RequiresCarService注解显式声明。不然测试跑起来,Service没启动,你查半天都不知道问题出在哪。

模拟器测试:低成本验证的第一道防线

模拟器测试,说白了就是在你的开发机上跑一个虚拟的车载系统。它不能替代真机测试,但能帮你快速发现低级错误。

我记得有一次,团队里一个新同事提交了一段代码,在模拟器上跑得好好的,结果上车就崩。后来发现是代码里写死了某个硬件的路径,模拟器上有这个路径,真机上没有。这种问题,模拟器是测不出来的。

那模拟器到底能测什么?

测试类型 模拟器支持度 我的建议
UI布局与交互 可以放心用,尤其是多分辨率适配
CarService API调用 基本功能可测,但HAL层行为是模拟的
车辆属性变化 只能测API调用是否报错,无法验证真实行为
性能与功耗 极低 别浪费时间,直接上真机

启动模拟器时,我建议用-gpu swiftshader_indirect参数。为什么?因为车载系统通常没有GPU直通,用软件渲染更接近真实环境。嗯,这个小技巧是我在调试一个动画卡顿问题时发现的。

硬件在环测试:让测试更真实

硬件在环测试,英文叫Hardware-in-the-Loop,简称HIL。说白了就是把真实的硬件设备接入测试环境,让测试代码跟真实硬件交互。

你想想看,模拟器里你调setTemperature(),它只是改了一个内存里的值。但在HIL测试中,这个调用会真的通过CAN总线发给空调控制器,然后你读取回来的温度值,是真实的物理反馈。

HIL测试的典型架构是这样的:

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  测试脚本        │────▶│  HIL测试平台      │────▶│  真实硬件设备    │
│  (Python/Java)   │     │  (如NI PXI/Vector)│     │  (空调/车窗/灯光)│
└─────────────────┘     └──────────────────┘     └─────────────────┘
        │                        │                         │
        │◀───────────────────────│◀────────────────────────│
        │  读取真实状态           │  采集传感器数据          │

我在项目中遇到过一个问题:某个车型的空调控制,在模拟器上测试全部通过,但HIL测试时发现,温度设定到26度以上时,实际出风温度反而下降了。后来排查发现,是HAL层对温度值的缩放因子搞错了。这种问题,没有HIL测试根本发现不了。

注意:HIL测试的成本很高。一套像样的HIL测试平台,少说几十万。所以别指望每个项目都上HIL。我的经验是:核心安全功能(如制动、转向)必须HIL测试,非安全功能(如娱乐、导航)可以靠模拟器+真机测试覆盖。

自动化测试脚本:把测试变成习惯

手动测试做一次两次还行,但车载系统迭代快,每次改代码都手动测一遍,谁也受不了。所以自动化测试脚本是必须的。

我常用的自动化测试框架是Python + ADB。为什么用Python?因为写起来快,而且跟车载系统的交互主要靠ADB命令,Python的subprocess模块用起来很顺手。

下面是一个简单的自动化测试脚本示例:

import subprocess
import time

def run_car_test():
    # 1. 安装测试APK
    subprocess.run(["adb", "install", "-r", "HVACTest.apk"])
    
    # 2. 启动测试
    subprocess.run([
        "adb", "shell", "am", "instrument",
        "-w", "com.example.hvac.test/androidx.test.runner.AndroidJUnitRunner"
    ])
    
    # 3. 拉取测试结果
    subprocess.run(["adb", "pull", "/sdcard/test_results.xml", "."])
    
    # 4. 解析结果
    with open("test_results.xml", "r") as f:
        content = f.read()
        if "failure" in content:
            print("测试失败,请检查日志")
        else:
            print("全部通过")

if __name__ == "__main__":
    run_car_test()

这个脚本很简单,但实际项目中,我会加上重试机制和日志收集。为什么?因为车载系统有时候会因为总线负载过高导致偶发失败,重试一次可能就过了。但如果是真问题,重试多少次都会失败,这时候日志就派上用场了。

小技巧:自动化测试脚本里,我习惯在每个关键步骤后加一个time.sleep(1)。别小看这一秒,车载系统的响应速度跟手机不一样,很多操作需要等待总线响应。不加这个延时,测试脚本跑得飞快,但结果全是假失败。

三种测试策略的取舍

说了这么多,你可能想问:到底该用哪种测试?

我的建议是分层覆盖:

  • 开发阶段:用模拟器测试,快速验证API调用和UI逻辑。这个阶段追求的是速度,不求全面。
  • 集成阶段:用自动化测试脚本,在真机上跑回归测试。这个阶段要覆盖所有核心功能。
  • 验收阶段:用HIL测试,验证硬件交互的正确性。这个阶段只测高风险功能。

我曾经见过一个团队,把所有测试都压在HIL上,结果每次改代码都要排队等HIL平台,开发效率极低。后来我帮他们调整了策略,把80%的测试放到模拟器和真机上,HIL只用来验证那20%的关键路径。效率一下子就上来了。

知识体系总览

下面这张图,是我对车载测试框架的整体理解。你可以把它当作一个参考,看看自己当前在哪个环节:

车载测试框架知识体系 CarTestManager 测试生命周期管理 模拟器测试 低成本快速验证 硬件在环测试 真实硬件交互 核心能力 • 管理CarService生命周期 • 模拟车辆属性读写 • 控制HAL层回调 适用场景 • UI布局与交互验证 • API调用正确性 • 多分辨率适配 适用场景 • 核心安全功能验证 • 硬件交互正确性 • 总线通信测试 自动化测试脚本(Python + ADB) 安装APK → 执行测试 → 拉取结果 → 解析报告 分层覆盖:开发阶段→模拟器 | 集成阶段→真机+自动化 | 验收阶段→HIL

这张图把整个测试框架分成了三层。最上面是三种测试手段,中间是各自的核心能力,最下面是自动化脚本把它们串起来。你仔细看会发现,CarTestManager其实贯穿了所有测试类型——不管是模拟器还是HIL,都需要它来管理测试环境。

好了,关于车载测试框架,我就讲这么多。记住一点:测试不是为了找bug,而是为了让你对代码有信心。没有测试的车载代码,就像没有安全带的汽车——也许能开,但出事就是大事。


公众号:蓝海资料掘金营,微信deep3321