9

示例语句:

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

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

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

4

5 回答 5

7

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

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

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

于 2019-01-12T02:13:28.473 回答
5

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

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

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

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

  3. 修正条件/决策覆盖 ( MC/DC )。结合决策和基本条件覆盖,“修改”使其还要求每个条件必须单独确定结果。Edwin Buck的答案是有效的 MC/DC 盖板(TTTT、FTTT、TFTT、TTFT、TTTF)。
    一般来说,在 N 个条件下,MC/DC 需要 N+1 个测试用例,而不是 2^N。因此,它在严谨性(测试每个条件)和效率(测试所有 2^4 可能没有必要)之间取得了很好的平衡。这背后的直觉正是亚当贝茨回答中的推理。

  4. 全条件覆盖:测试所有 2^N 个可能的组合。

于 2019-01-12T13:54:05.443 回答
2

您可能不需要执行所有 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

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

编辑:avandeursen 建议的更改

于 2019-01-12T00:40:58.590 回答
2

我会推荐以下方法

                       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 的重新排列,并且不依赖短路逻辑来确保捕获失败条件。(贾斯汀的回答也很好,但是通过在他的解决方案中为未检查的值选择真实值,您可以增加测试的表达能力并防止错误消息指示错误的非真实选项,如果您决定以某种方式报告哪个选项是错误的)。

于 2019-01-12T03:15:53.603 回答
2

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

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

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

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

“软件单元验证过程的目的是验证软件单元,以提供证据证明软件单元符合软件详细设计和非功能性软件要求。”</p>

另一组单元测试描述,对于对质量特别是安全性有更高要求的软件,可以参考 ISO 26262“道路车辆——功能安全——”,第 6 部分:软件级别的产品开发”,第 9 章,表 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。

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

再看你的问题:

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

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

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

错误类别

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”(错误类 ORF),则使用条件覆盖,以及测试向量“TTTT”和“FFFF”,将为您提供 100% 的条件覆盖和100% 的决策或分支覆盖率。但是你不会发现错误。MCDC 将揭示这个问题。

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

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

有几种可用的定义,主要谈论“独特原因” MCDC,忽略了一个事实,同时“掩蔽 MCDC”和“唯一原因 + 掩蔽 MCDC”也是经过认证和批准的标准。对于那些你需要忘记从真值表上的黑盒视图开始的所有教程。谈到结构覆盖或测试,应该很清楚,只有 WhiteBox 视图才有效。而且,如果您碰巧使用具有布尔快捷评估的语言(例如在 Java、C 或 C++ 中)进行开发,那么 WhiteBox 视图是强制性的会更加明显。


对于您的布尔表达式/决策(“abcd”),并应用布尔快捷评估,总共有 16 个唯一原因 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 个唯一原因 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

于 2019-05-04T17:29:06.947 回答