测试用例基础:结构、输入输出与断言

说到测试用例,我见过不少新手上来就写代码,结果跑起来全是坑。其实测试用例的结构化设计,比写代码本身更重要。今天我们就聊聊这个基础,但绝对关键的话题。

测试用例的标准结构

一个规范的测试用例,说白了就三块:前置条件执行步骤预期结果。我在项目中见过最糟糕的测试,就是只写了「点击按钮,看效果」——这根本不算测试用例。

我个人习惯把测试用例拆成五个字段:

字段 说明 示例
用例ID 唯一标识,方便追溯 TC_EDA_001
测试描述 一句话说清测什么 验证网表解析模块能正确读取标准Verilog文件
前置条件 测试开始前必须满足的状态 已加载标准库文件,输入网表路径合法
测试步骤 按顺序执行的操作 1. 调用parseNetlist() 2. 传入test.v文件
预期结果 可验证的最终状态或输出 返回对象包含3个module定义,无报错

嗯,这里要注意:前置条件经常被忽略。我曾经因为漏写「数据库已初始化」这个条件,导致一个测试跑了三小时全失败,最后发现是环境没准备好。从那以后,我每个用例都强制写前置条件。

输入输出定义:别让模糊毁了你

输入输出定义,是测试用例的「接口契约」。你想想看,如果连输入是什么格式都没说清楚,测试结果怎么可能可靠?

在EDA前端测试中,输入通常分三类:

  • 文件输入:Verilog网表、SPEF寄生参数、SDC约束文件
  • 参数输入:命令行选项、配置文件中的键值对
  • 交互输入:用户点击、键盘事件(GUI测试时)

输出定义更讲究。我建议你明确三点:

  1. 输出类型:是返回对象、写文件、还是打印日志?
  2. 输出格式:JSON结构、文本行数、数值精度
  3. 边界条件:空输入时输出什么?异常时抛出什么异常?

核心原则:输入输出定义必须精确到「能写进代码断言」的程度。比如「返回一个列表」就不够,要写成「返回一个长度为3的列表,每个元素是Module对象,包含name和port_count属性」。

断言机制:测试的「裁判」

断言,就是替你说「这结果对不对」的裁判。没有断言的测试,跑完等于没跑。

我常用的断言分四个层次:

断言类型 适用场景 代码示例
相等性断言 验证返回值是否等于预期 assertEqual(result, expected)
包含性断言 验证输出中是否包含特定内容 assertIn("ERROR", log_text)
类型断言 验证返回对象的类型 assertIsInstance(obj, Netlist)
异常断言 验证是否抛出预期异常 assertRaises(FileNotFoundError)

举个例子,我在测试网表解析器时,写过这样的断言:

def test_parse_verilog_module():
    # 前置条件:准备一个简单的Verilog文件
    test_file = "test_module.v"
    with open(test_file, "w") as f:
        f.write("module adder(a, b, sum); input a, b; output sum; endmodule")
    
    # 执行:调用解析函数
    parser = NetlistParser()
    result = parser.parse(test_file)
    
    # 断言:验证结果
    assert result is not None, "解析结果不应为空"
    assert len(result.modules) == 1, "应解析出1个module"
    assert result.modules[0].name == "adder", "module名称应为adder"
    assert len(result.modules[0].ports) == 3, "应有3个端口"
    assert "a" in result.modules[0].ports, "端口a应存在"

我的小技巧:断言信息一定要写清楚。别只写assert x == y,加上描述信息,比如assert x == y, f"期望{y},实际{x}"。这样失败时一眼就能看出问题。

知识体系框架

下面这张图,是我对测试用例基础的理解。它把结构、输入输出、断言串在了一起:

测试用例基础 测试用例结构 用例ID 测试描述 前置条件 测试步骤 预期结果 输入输出定义 输入:文件/参数/交互 输出:类型/格式/边界 精确到可断言 异常情况定义 断言机制 相等性断言 包含性断言 类型断言 异常断言 三者关系:结构定义框架,输入输出明确边界,断言验证结果 缺一不可,共同构成可执行的测试用例

避坑指南:我踩过的三个坑

最后分享几个实战教训:

  • 坑一:断言写得太宽松。我曾经只检查返回值不为空,结果返回了一个空列表,测试通过了,但功能根本没实现。现在我会断言具体数值。
  • 坑二:输入定义不完整。有一次测试网表解析,我忘了定义「文件编码格式」,结果在Linux上跑通过,Windows上全挂——因为默认编码不同。
  • 坑三:忽略异常路径。我习惯只测「正常流程」,直到线上出现非法输入导致崩溃。现在每个用例我都会加一个异常断言。

重要提醒:测试用例不是写给别人看的,是写给未来的自己看的。三个月后你回来看代码,如果看不懂自己写的测试,那说明当时偷懒了。结构清晰、断言明确、输入输出完整——这三条做到了,你的测试用例就是好用例。

好了,测试用例的基础就聊到这儿。记住:结构是骨架,输入输出是血肉,断言是灵魂。三者缺一不可。


专注资料整理