在一条语句中测试许多 AND/OR 条件的覆盖率

Test coverage for many AND/OR conditions in one statement

示例语句:

if (conditionA && conditionB && conditionC && conditionD) {
    return true;
}

我可以为所有 2^4 组合编写单元测试,但如果添加更多条件,这很容易失控。

我的单元测试策略应该是什么来覆盖像这样的语句的所有条件?有没有其他方法可以让代码更健壮?

您可能不需要执行所有 2^4 个条件,因为例如,如果 A 为假,则甚至不会检查其他条件。你可能只需要 5

    A   B   C   D
    F   X   X   X
    T   F   X   X
    T   T   F   X
    T   T   T   F
    T   T   T   T

但正如另一个戴夫所说,根据您的代码,您可能不需要测试所有条件。想想你的测试objective,看看什么是合适的

编辑:avandeursen 建议的更改

我对这种情况的看法是 1 条快乐的道路和 4 个潜在的失败点。如果每个条件对于允许返回 true 都是关键的,那么这样写是合理的:

  1. 单个快乐路径单元测试,逻辑 returns 为真的唯一情况。并且
  2. 对可能导致检查失败的每个变量进行单元测试,断言单个变量有能力阻止条件通过。

我知道可以编写通过这些检查的逻辑,但实际上 returns 当多个变量为假时为真......但我真的不会担心这种情况,除非你正在处理宇宙飞船或涉及生/死的东西。在几乎所有情况下,测试人员只是在测试实现是否因任何变量失败而失败。

我会推荐以下方法

                       A B C D
testTypicalCall()   -> T T T T
testAisFalseFails() -> F T T T
testBisFalseFails(  -> T F T T
testCisFalseFails() -> T T F T
testDisFalseFails() -> T T T F

这捕获了您可能失败的四种独立方式,并且可以推断出如果其中两种方式组合发生,那么至少应该触发一种失败测试。

在未来的 if 语句重构中,它还可以防止 A、B、C 和 D 的重新排列,并且不依赖短路逻辑来确保捕获失败条件。 (贾斯汀的回答也很好,但是通过在他的解决方案中为未检查的值选择真值,您可以增加测试的表达能力并防止错误消息指示错误的非真选项,如果您决定以某种方式报告哪个选项是假)。

关于这个主题已经写了很多,你的问题似乎需要MC/DC。

有一个由多个条件组成的谓词导致了一个决定。 应用于问题中谓词的众所周知的覆盖标准包括:

  1. 决策覆盖率:确保整个谓词一次为真,一次为假。
    这导致两个测试用例,例如 (T,T,T,T) 和 (F,T,T,T)。

  2. 基本条件覆盖:确保每个条件都为真和假。
    这也可以通过两个测试用例来实现:(T,T,T,T) 和 (F,F,F,F)。
    请注意,基本条件覆盖不一定意味着决策覆盖(示例:"P AND Q" 测试用例 (T,F) 和 (F,T) 满足基本条件覆盖,但两者都评估为 F,因此未达到 100% 的决策覆盖率)。

  3. 修改后的条件/决策覆盖率(MC/DC). Combination of decision and basic condition coverage, "modified" so that it also requires that each condition must individually determine the outcome. The 是有效的 MC/DC 覆盖率(TTTT、FTTT、TFTT、TTFT、TTTF)。
    一般来说,N 个条件 MC/DC 需要 N+1 个测试用例,而不是 2^N。因此,它在严谨性(测试的每个条件)和效率(可能没有必要测试所有 2^4)之间取得了很好的平衡。这背后的直觉正是.

  4. 中的推理
  5. 全条件覆盖:测试所有 2^N 种可能的组合。

作为我的回答的介绍,我想再次解释一下,我们为什么要进行软件测试。大多数测试人员的误解真的很大

  • 我们不会测试软件以证明它没有错误(这是不可能的。已经用于次要的复杂软件)
  • 我们执行“建设性测试”,以证明功能是可接受的table并且要求或功能是“有效的”。
  • 最重要的是:我们试图找到尽可能多的错误(“破坏性测试”)。我们永远找不到所有的错误。可以应用 SRGM 来显示,应该进行多深的测试。

然后,这已经是您问题“我的单元测试策略应该是什么?”的一部分的答案

我将引用 Automotive SPICE (PAM 3.1),一种众所周知且经过验证的过程模型,过程 SWE.4,软件单元验证:

“The purpose of the Software Unit Verification Process is to verify software units to provide evidence for compliance of the software units with the software detailed design and with the non-functional software requirements.”

另一组单元测试说明,针对对质量特别是安全性要求较高的软件,可取自ISO 26262《道路车辆——功能安全——》,第6部分:软件层面的产品开发,章节9, tables 10, 11, 12

软件单元测试的方法

Requirements-based test
Interface test
Fault injection test
Resource usage test
Back-to-back comparison test between model and code, if applicable

软件单元测试的测试用例导出方法

Analysis of requirements
Generation and analysis of equivalence classes
Analysis of boundary values
Error guessing

现在最重要的是,回答问题的第二部分,您应该进行结构覆盖分析(不是结构测试),以评估测试用例的完整性并证明没有意外功能。永远不要混淆结构测试和结构覆盖。

因此,您将测试和检查是否满足要求,并且您将进行结构覆盖测量以证明这一点。如果覆盖率结果太低,那么你将添加更多的测试用例。

推荐的覆盖率指标是 MCDC。

当然,您也可以select众多其他覆盖方法之一。但是你应该在你的测试策略中给出一个理由,你为什么这样做。

再看看你的问题:

if (conditionA && conditionB && conditionC && conditionD) 
{
    return true;
}

看来您是在征求结构测试的建议。我也会回答这个问题,但请注意,使用这种方法,您只是在测试编译器是否正常工作。

对于手头的布尔表达式(以及其他更复杂的表达式),您可能永远不会发现以下错误之一:

错误classes

Expression Negation Fault (ENF)
Sub-Expression Negation Fault (SENF)
Sub-Expression Omission Fault (SEOF)
Literal Negation Fault (LNF)
Literal Omission Fault (LOF)
Literal Reference Fault (LRF)
Literal Insertion Fault (LIF)
Operator Reference Fault (ORF)
Stuck-at-1 Fault (SA1) 
Stuck-at-0 Fault (SA0)
Parenthesis Insertion Fault (PIF)
Parenthesis Omission Fault (POF)
Parenthesis Shift Fault (PSF)

记住,我一开始说的,测试要发现错误(破坏性测试)。否则你可能会错过上述错误。

示例:

如果您的要求最初打算在表达式中使用“OR”而不是“AND”(错误 class ORF),请使用条件覆盖,测试向量“TTTT”和“FFFF” , 将为您提供 100% 的条件覆盖率和 100% 的决策或分支覆盖率。但是你不会发现这个错误。 MCDC 会揭露问题。

您还提到了测试所有组合(MCC、多条件覆盖)的可能性。对于 4 个变量,这是可以的。但是对于更多变量,测试执行持续时间将呈几何级数增长。这是不可管理的。这就是定义 MCDC 的原因之一。

现在,让我们假设您的示例语句是正确的,并返回到基于 MCDC 的结构测试的测试用例定义,用于您的表达式。

有多种定义,主要是在谈论“Unique Cause”MCDC,而忽略了事实,同时“Masking MCDC”和“Unique cause + Masking MCDC”也是认证和批准的标准。对于那些你需要忘记所有以关于真相的 BlackBox 视图开始的教程 table。谈到结构覆盖或测试,应该清楚的是,只有 WhiteBox 视图才能工作。而且,如果您碰巧使用具有布尔快捷方式评估的语言进行开发(例如 Java、C 或 C++),则更明显的是必须使用 WhiteBox 视图。


对于您的布尔值 expression/decision(“abcd”),并应用布尔值快捷方式评估,总共有 16 个 Unique Cause MCDC 测试对:

1   Influencing Condition: 'a'  Pair:  0, 15   Unique Cause
2   Influencing Condition: 'a'  Pair:  1, 15   Unique Cause
3   Influencing Condition: 'a'  Pair:  2, 15   Unique Cause
4   Influencing Condition: 'a'  Pair:  3, 15   Unique Cause
5   Influencing Condition: 'a'  Pair:  4, 15   Unique Cause
6   Influencing Condition: 'a'  Pair:  5, 15   Unique Cause
7   Influencing Condition: 'a'  Pair:  6, 15   Unique Cause
8   Influencing Condition: 'a'  Pair:  7, 15   Unique Cause
9   Influencing Condition: 'b'  Pair:  8, 15   Unique Cause
10  Influencing Condition: 'b'  Pair:  9, 15   Unique Cause
11  Influencing Condition: 'b'  Pair: 10, 15   Unique Cause
12  Influencing Condition: 'b'  Pair: 11, 15   Unique Cause
13  Influencing Condition: 'c'  Pair: 12, 15   Unique Cause
14  Influencing Condition: 'c'  Pair: 13, 15   Unique Cause
15  Influencing Condition: 'd'  Pair: 14, 15   Unique Cause

得到推荐的MCDC测试集(解法不止一种):

Test Pair for Condition 'a':    0  15   (Unique Cause)
Test Pair for Condition 'b':    8  15   (Unique Cause)
Test Pair for Condition 'c':   12  15   (Unique Cause)
Test Pair for Condition 'd':   14  15   (Unique Cause)

测试向量:最终结果:0 8 12 14 15

 0:  a=0  b=0  c=0  d=0    (0)
 8:  a=1  b=0  c=0  d=0    (0)
12:  a=1  b=1  c=0  d=0    (0)
14:  a=1  b=1  c=1  d=0    (0)
15:  a=1  b=1  c=1  d=1    (1)

没有布尔快捷方式评估,很明显,您只有 4 个 Unique Cause MCDC 测试对:

1  Influencing Condition: 'a'  Pair:  7, 15   Unique Cause
2  Influencing Condition: 'b'  Pair: 11, 15   Unique Cause
3  Influencing Condition: 'c'  Pair: 13, 15   Unique Cause
4  Influencing Condition: 'd'  Pair: 14, 15   Unique Cause

导致一个确定性解决方案:

测试向量:最终结果:7 11 13 14 15

 7:  a=0  b=1  c=1  d=1    (0)
11:  a=1  b=0  c=1  d=1    (0)
13:  a=1  b=1  c=0  d=1    (0)
14:  a=1  b=1  c=1  d=0    (0)
15:  a=1  b=1  c=1  d=1    (1)

我希望,我可以进一步阐明这个问题。

如果您想在工具支持下更详细地探索 MCDC,您可以查看

MCDC