1. 数据流测试
1.1 数据流测试的背景和动机
最早的电子计算机是用于战斗中的弹道计算,核心任务是数据处理。软件运行过程中,数据的流动与处理是核心,因此软件测试中产生了“数据流测试”的概念。传统的结构化测试只关注程序结构,如路径覆盖、条件覆盖等,而数据流测试专注于变量的定义与使用是否正确。
✅结论:数据流测试专注于变量的定义和使用是否正确
1.2 变量的两类操作
在前面的文章中,我们只强调了程序结构性的覆盖,我们并没有关心数据流图中“结点”上到底是什么东西,而数据流我们是关注关于这些“节点”中变量的操作是否正确。
对变量的操作可以分成定义和使用两种:
- 定义(Definition):
- 向变量赋值、初始化。
- 表示把值“塞”进内存中。
- 例:
x = 42
(将值42赋给变量x,是对x的定义)。
- 使用(Use):
- 读取变量的值或以其参与运算、判断、控制流等。
- 包括赋值右值、条件语句、循环条件等。
- 例:
z = x * 2
(x为使用,z为定义);if (x > 0)
(x被使用)。
我们来看这个结构:
在节点 1 有 x = 42
这是关于变量 X
的定义。在节点 5 Z = X * 2
这是关于变量 X
的使用,也是关于变量 Z
的定义,节点 6 也是一样。
从这张图我们可以了解,变量定义了,当然以后就要用它。用的地方可能一个也可能有多个。变量也可以在不同的地方定义,而它使用的地方也有可能会交叉,因此构建了定义和使用之间比较复杂的一些关系。
✅结论:变量可以在不同的地方被定义和使用,这会导致复杂的关系
1.3 DU 对(Definison-Use 对)
为了理清变量定义和使用之间的关系,我们首先引入以下几个定义:
- def(n):在节点
n
上定义的所有变量集合(definition set)。 - use(n):在节点
n
上使用的所有变量集合(use set)。 - def(e):在边
e
上定义的变量。 - use(e):在边
e
上使用的变量。
我们定义 (li, lj) 是变量 v 的 DU 对 ,表示变量 v 在节点 li
上被定义,在节点 lj
上被使用,要求它们之间的路径是定义清晰的路径(Definition-Clear Path) ,即从 li
到 lj
的路径上,变量 v
没有被重新定义。
注:若中间某个节点重新定义了变量 v,则前面
li
的定义就被“屏蔽”,无法传到lj
。
这一点大家很容易理解。假如我们定义被再重新定义以后,前面的定义就没有用了,所以数据流的可达是指有一条定义清晰的路径,可以从 li
到 lj
。
1.4 DU 路径(Definition-Use Path)
DU 路径是指定义清晰且简单的一条子路径,下面这张图阐释 DU 路径与路径集合的具体定义和符号表示:
-
DU路径(DU path):
- 是一条简单路径(无重复节点);
- 是定义清晰路径;
- 从
ni
定义点到nj
使用点。
-
DU路径集合记法:
du(ni, nj, v)
:从ni
定义 v 到nj
使用 v 的所有DU路径集合;du(ni, v)
:从ni
定义v到任意使用v的地方的所有路径集合。
2. 数据流覆盖准则
2.1 三种数据流覆盖的概念
-
定义覆盖(All-Defs Coverage)
- 要求每一个变量的每一个定义点至少参与一次的DU对测试;
- 即每一个定义过的变量,都要至少使用一次。
-
引用覆盖(All-Uses Coverage)
- 要求每个变量的每个使用点至少通过一次清晰路径到达;
- 强调所有使用点都被覆盖;
- 因为有使用就必须有对应的定义,否则程序运行就会出错。
-
所有定义-引用路径覆盖(All DU-Paths Coverage)
- 最强的准则;
- 要求每一个定义-引用对之间的所有清晰路径都被覆盖;
- 对变量v的每一个定义点,必须考虑其到所有使用点的所有清晰路径。
2.2 数据流覆盖的示例
我们接着第一节的例子,针对变量 x
进行数据流覆盖:
对于定义覆盖,我们只需要保证变量 x
的每一个定义点至少参与一次 DU 对测试。所以我们只需要一条测试用例,并保证测试用例通过定义点 1 即可。
对于引用覆盖,我们需要保证变量 x
的每个使用点至少通过一次清晰路径到达。所以我们只需要两条测试用例,并保证测试用例通过使用点 5 和 6 即可。
对于定义引用覆盖,我们需要保证变量 x
的每一个定义-引用对之间的所有清晰路径都被覆盖。所以我们只需要四条条测试用例,覆盖所有 1 → 5 和 1 → 6 的情况。