第四章 测试数据生成:随机数据生成、边界值分析、等价类划分

各位好,我是老张。在EDA前端测试这个领域摸爬滚打了十几年,今天咱们来聊聊测试数据生成。

说实话,很多新手写测试用例,最头疼的就是数据怎么来。随便填几个数?那叫碰运气。我见过太多项目,因为测试数据覆盖不全,流片回来才发现bug。嗯,今天就把我压箱底的三板斧教给你。

4.1 随机数据生成——别让测试变成“玄学”

随机数据生成,说白了就是让计算机帮你造数据。但这里有个坑:纯随机不等于有效测试

我个人习惯的做法是:先定好数据范围,再随机。比如你要测一个加法器,输入范围是0到255,那就别随机出负数来。

核心原则:随机数据要符合业务约束,否则就是垃圾数据。

来看个实际例子。我在项目中遇到过这样一个场景:测一个FIFO的读写逻辑。如果完全随机,可能1000次测试里只有几次触发了满状态。怎么办?

// 伪代码示例:带约束的随机生成
class FifoTestData;
  rand bit [7:0] data_in;
  rand bit      write_en;
  rand bit      read_en;
  
  // 约束:不能同时读写同一个地址
  constraint valid_op {
    !(write_en && read_en);
  }
  
  // 约束:写操作时数据必须在有效范围
  constraint data_range {
    data_in inside {[0:255]};
  }
endclass

你想想看,加了约束之后,随机出来的数据是不是靠谱多了?

小技巧:随机种子要可复现。我习惯用时间戳做种子,但调试时固定种子。这样既能保证每次跑不一样,又能复现bug。

4.2 边界值分析——bug最喜欢藏的地方

为什么边界值这么重要?我告诉你一个秘密:80%的bug都藏在边界上

为什么会这样?因为程序员写代码时,最容易在边界条件上犯迷糊。比如“<=100”写成了“<100”,这种错误太常见了。

边界值分析的核心思想很简单:别只测中间值,要测边界和边界附近的值

边界类型 测试值示例 说明
最小值 0 数据范围的下界
最大值 255 数据范围的上界
最小值-1 -1 下界外一点
最大值+1 256 上界外一点
边界附近 1, 254 边界内紧邻值

我曾经遇到过一个案例:一个计数器模块,规格说计数到1000自动清零。测试时只测了999、1000、1001,结果没问题。但后来发现,当计数从1000清零后,下一个时钟周期竟然变成了1而不是0。为什么?因为清零逻辑和递增逻辑的时序没处理好。这就是边界附近的“组合爆炸”问题。

注意:边界值分析不是万能的。它擅长发现单一边界问题,但对多边界组合问题效果有限。这时候需要结合等价类划分。

4.3 等价类划分——把无穷变成有限

等价类划分,说白了就是把无限多的输入,分成有限的几类。每一类里,随便挑一个值测,效果都一样。

举个例子:一个模块接收0-100的整数。按等价类划分,可以分成:

  • 有效等价类:0-100之间的整数
  • 无效等价类:小于0的整数、大于100的整数、非数字字符

每个等价类里,你只需要测一个值就够了。比如有效类里测50,无效类里测-1、101、'abc'。这样就把无穷多的输入,压缩成了几个测试用例。

我刚开始做测试时,总觉得这样测不够全面。后来发现,等价类划分的精髓在于“等价”二字——同一类里的数据,对程序的行为影响是一样的。你测了50,就没必要再测51、52了。

实战经验:等价类划分要和边界值分析配合使用。先划分等价类,再在每个类的边界上重点测试。这样既全面又高效。

4.4 三种方法的组合策略

这三种方法不是孤立的。我一般这样组合使用:

  1. 先用等价类划分:把输入空间分成几个大类
  2. 再用边界值分析:在每个类的边界上补充测试
  3. 最后用随机生成:在类内部随机采样,增加覆盖率

你想想看,这样是不是既保证了全面性,又提高了效率?

下面这张图是我自己总结的测试数据生成流程,你可以参考一下:

测试数据生成流程 1. 输入分析 2. 等价类划分 3. 边界值分析 4. 随机数据生成 5. 覆盖率检查 6. 生成测试用例 覆盖率不足时反馈调整

这张图我用了好多年,每次带新人都会拿出来讲一遍。你仔细看看,覆盖率检查是个反馈环——如果覆盖率不够,就回到前面重新调整等价类划分或随机策略。

4.5 实战中的避坑指南

最后,分享几个我踩过的坑:

  • 别迷信随机测试:随机测试只能发现“意料之外”的bug,但发现不了“设计缺陷”。我见过有人跑了100万次随机测试,结果一个bug没找到,但换了个边界值测试立马就崩了。
  • 等价类划分要细:别把一个大类当成一个等价类。比如“0-100的整数”,其实可以再细分成“0-50”和“51-100”两个子类,因为程序可能对这两个区间有不同的处理逻辑。
  • 边界值要测“内外”:不仅要测边界上的值,还要测边界外一点点。比如边界是100,那就测99、100、101。我曾经因为只测了100没测101,漏掉了一个溢出bug。

我的习惯:每次写完测试用例,我都会问自己三个问题:

  1. 所有等价类都覆盖了吗?
  2. 每个边界都测了内外两侧吗?
  3. 随机数据有约束吗?

三个问题都回答“是”,我才敢说测试用例写完了。

好了,关于测试数据生成,今天就聊这么多。记住:好的测试数据,不是靠运气,而是靠方法。下次写测试用例时,试试这三板斧,你会发现bug无处可藏。


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