本主题说明如何查看使用 C++test 运行的测试的覆盖率信息。跟踪应用程序级别的单元测试以及手动或自动测试的覆盖范围。
查看覆盖率统计信息可帮助您衡量当前测试套件的覆盖率,并确定应添加哪些其他测试用例。 C++test 可以报告各种代码覆盖率类型、包括函数、行、路径、基本块、决策(分支)、简单条件和 MCDC 覆盖率指标。
内容包括:
在 GUI 中分析覆盖率
覆盖率摘要
要在 C++test 执行单元测试用例后查看覆盖率信息摘要:
- 打开覆盖率视图。
- 如果无法打开,请选择 Parasoft> 显示视图 > 覆盖率。
- 在【覆盖率】视图工具栏中的下拉菜单中,选择 类型> [所需的覆盖类型]。
- 有关可用覆盖率类型的说明请参见了解覆盖率类型。
项目的覆盖率统计信息以及所有分析的文件和函数将显示在【覆盖率】视图中。如果测试执行后没有显示覆盖率,请检查1)启用了覆盖率,以及2)您正在显示配置 C++test 计算的覆盖率类型。视图的工具栏指示正在显示的覆盖率指标。
您可以使用可用的 GUI 控件对结果进行排序和搜索。
注释源代码
在编辑器中打开经过测试的文件时,C++test 将使用绿色突出显示指示覆盖了哪些代码,使用粉色突出显示指示未覆盖哪些代码。不可执行的行不会突出显示。
请注意您需要双击一个函数才能启动路径覆盖率视图。
注释
C++test 可以计算的覆盖元素(路径、块等)的数量是有限制的。当给定级别(函数、文件或项目)上的实际元素数超过限制 2147483647 时,C++test 将在报告中显示给定元素的 "N/A"。在这种情况下,覆盖率视图将包含适当的报文,例如 [无路径]、[无障碍]。
从覆盖元素到测试用例的追溯
了解哪些测试用例与每个覆盖元素相关可以帮助您更好地评估如何扩展测试用例以提高覆盖率。
查看哪些测试用例与覆盖元素相关:
- 在编辑器中选择该覆盖项。
- C++test 将考虑当前选择/光标位置,而不是鼠标指针位置。
- 右键单击该选择,然后选择 Parasoft> C++test> 显示覆盖元素的测试用例。所有相关的测试用例将在【测试用例浏览器】中突出显示。
指定测试用例的覆盖率数据
分析单个测试用例的覆盖率:
- 在【测试用例浏览器】中,选择要分析其覆盖率的测试用例。
- 默认情况下会打开【测试用例浏览器】。如果不可用,可以通过选择 Parasoft> Show View> Test Case Explorer打开它。关了解和浏览【测试用例浏览器】的详细信息,请参见探索 C++test 用户界面。
- 在【覆盖率】视图中,单击 【测试用例浏览器】过滤器中的【与之同步】选择 (双齿轮按钮)。
覆盖率统计数据和覆盖率突出显示(在代码编辑器中)只会为选定的测试用例计算/显示。
提示:使用工具栏按钮和菜单浏览覆盖率
覆盖率视图提供了几个按钮和菜单命令,以帮助您浏览所报告的覆盖率详细信息。
工具栏右侧的按钮允许您折叠、删除、搜索结果以及与【测试用例浏览器】中的当前选择同步。
此外,以下工具栏按钮允许您显示/隐藏覆盖的元素,显示/隐藏未覆盖的元素,突出显示下一个路径(仅适用于路径覆盖)和突出显示上一个路径(仅适用于路径覆盖)。
下拉菜单提供的命令使您可以按名称或覆盖率的升序/降序对结果进行排序,以及选择所需的覆盖率类型。
注释:启动时的覆盖率查看结果
为了优化性能,【覆盖率】视图以未初始化的模式启动—仅显示项目级别的覆盖率摘要。此模式以灰色项目图标指示:
这种模式下的覆盖率可能不是当前的 — 例如,如果源代码在这个 IDE 之外被修改。您可以通过简单地扩展项目节点来更新覆盖率视图。或者,您可以等待视图在执行期间自动更新。
分析报告的覆盖率
有关配置,生成和查看报告的详细信息,请参见 查看结果。
了解覆盖率类型
C++test 持以下覆盖率类型:
- 行覆盖率
- 语句覆盖率
- 块覆盖率
- 路径覆盖率
- 决策(分支)覆盖率
- 修改的条件/决策覆盖率(MC/DC)
- 简单条件覆盖率
- 函数覆盖率
- 调用覆盖率
为了帮助理解 C++test 如何处理这些覆盖率类型,请务必阅读并理解下表中的术语:
概念 | 说明 |
---|---|
基本块 | 非分支语句序列;没有控制流路径分支的线性代码序列。 |
路径 | 从函数入口到出口点的一系列独特的基本块。 |
决策/分支 | 决策/分支是可能在代码的分支点做出的控制流决策。C++test 将 if-else、for、while、do-while 和 switch 指令视为分支点。C++test 不将诸如异常处理程序(throw-catch 语句)之类的动态分支点考虑在内。 |
布尔表达式 | 在 C++ 中,布尔表达式只是具有 'bool' 类型的表达式。 在 C 语言中,C++test 将以下内容视为布尔表达式:
|
MC/DC 决策 | 由条件和零个或多个布尔运算符组成的顶级布尔表达式。C++test 对源代码中所有布尔非常数表达式(构造函数初始化程序和函数默认参数除外)计算 MC/DC 和 SCC。 |
条件 | 原子布尔非常数表达式,是 MC/DC 决策的一部分。 如果 MC/DC 决策的子表达式不包含布尔运算符(&&, ||, !),则将其视为条件。 如果给定的原子表达式在一个决策中出现多次,则每次出现都是一个不同的条件。 |
函数覆盖率
指示在执行过程中至少访问了源代码中的函数一次。如果所有函数至少访问过一次,将获得完整的 100% 功能覆盖率。
调用覆盖率
表明在程序运行时执行了多少个定义的函数或方法调用。如果执行了所有函数或方法调用,则可获得 100% 的完整调用覆盖率。
局限性:
- 构造函数调用(显式和隐式)均不包括在调用覆盖率计算中。
- 隐式析构函数调用不包括在调用覆盖率计算中。
- 调用覆盖率计算中不包括预定义和重载的 C++ 运算符。
示例:
class A { public: A() {} A(const A&) {} A(int val) {} A& foo() { return *this; } ~A() {} }; void funcA(const A& a) {} A& operator+(const A & a, const A & b) {} void defaultConstructorCall() { A objA; // Default constructor call - not included A objArr[10]; // Default constructor calls - not included A objB = objA; // Copy constructor call - not included A objC = 10; // Implicit call of A(int) constructor - not included funcA(10); // Implicit call of A(int) constructor - not included A res = objA + objA; // Operator + call - not included // Copy constructor call - not included A* objD = new A(); // Default constructor call - not included // Operator new call - not included delete objD; // Destructor call - not included // Operator new call - not included A* objE = new A[10]; // Default constructor calls - not included // Operator new call - not included delete[] objE; // Destructor calls - not included // Implicit destructor calls - not included }
行覆盖率
指示控制流至少一次访问了多少源代码的可执行。如果所有可执行行至少访问过一次,将获得完整的 100% 行覆盖率。
语句覆盖率
指示控制流至少一次访问了多少源代码的可执行源代码语句。如果所有可执行行至少访问过一次,将获得完整的 100% 语句覆盖率。
块覆盖率
与【行覆盖率】相似,除了【块覆盖率】外,测量代码的单位是基本块(请参见上表中此术语的定义)。这表明控制流至少一次访问了源代码中的几个基本块。
路径覆盖率
指示给定函数中的每个可能路径后面是否跟着控制流。用于选出路径的分支点(请参见上表中对该术语的解释)与【决策(分支)覆盖率】中的相同。
由于循环引入了无限数量的路径,因此该措施仅考虑了有限数量的循环可能性。C++test 考虑了 while 循环和 for 循环的两种可能性:零次和至少一次重复。
在源代码编辑器中,C++test 一次突出显示一个路径。要在源代码编辑器中查看路径,请在【覆盖率】视图中双击相应的功能节点。 要在函数的路径之间导航,请使用【覆盖率】视图工具栏中的突出显示下一个元素和突出显示上一个元素 按钮。
以提示 C++test 仅显示未发现的路径,请禁用【覆盖率】视图工具栏中的突出显示覆盖元素按钮。以提示 C++test 仅显示覆盖的路径,请禁用 突出显示未覆盖的元素。请注意仅当突出显示未覆盖元素按钮被禁用时,突出显示下一个/上一个元素按钮才会在意外路径中进行迭代。
决策(分支)覆盖率
指示源代码控制流中经过了多少分支。当所有分支机构的每个决策都至少执行一次所有可能的结果时,将获得 100% 的覆盖率。
C++test 考虑以下语句类型在源代码中的分支点: if-else
、 for
、 while
、 do-while
、 和 switch
。C++test 不将诸如异常处理程序(throw-catch
语句)之类的动态分支点考虑在内。
如果文件中没有任何决策,则 C++test 报告该度量指标不可用(使用 "N/A" 标签)。
修改的条件/决策覆盖率(MC/DC)
MC/DC 符合国际技术标准 DO-178B(RTCA),该标准指定了航空业中关键任务设备和系统的软件认证标准。这包括实时嵌入式系统。
根据 DO-178B 标准,必须满足以下三个条件才能获得完整的(100%) MC/DC 覆盖率:
- 每个决策都至少取得了所有可能的结果。
- 决策中的每个条件都至少取得了所有可能的结果。
- 决策中的每个条件都会独立影响该决策的结果。
由于 C++test 认为每个条件和决策在 MC/DC 覆盖范围内只能有两个结果(对或错),因此仅检查紧接在上面的第三个选项(c)- 因为点(c)暗示了条件(a)和(b)。在保持所有其他可能条件不变的情况下,一个条件可以独立地影响决策的结果。为了测试一个给定的条件,C++test 在以下情况下寻找测试用例:
- 测试的条件既有真有假。
- 决策中的其他条件不会更改(或不会评估,因为 C/C++ 中的运算符具有短路逻辑)。
- 决策的结果会改变。
因此,为了计算 MC/DC 率,C++test 使用以下公式
MC/DC[%] = m/n
其中, m
是证明可以独立影响决策结果的布尔条件的数量,n
是决策中条件的总数。
请注意为了覆盖单个条件,至少需要执行两个测试用例:一个测试条件的条件为true
,第二个测试条件的条件为false
。
MC/DC 示例
例如,考虑以下代码:
if (a && (b || c)) // [...]
有如下三个简单的条件:'a', 'b’ 和 'c'。结果,n(在 m/n MC/DC 公式中)等于 3。
现在,假设执行了以下测试用例:
id | a | b | c | a && (b || c) |
---|---|---|---|---|
1 | true | true | false | true |
2 | false | true | false | false |
3 | true | false | false | false |
为了计算 MC/DC,C++test 查找成对的测试用例,其中1)给定决策的评估值不同,2)仅一个条件的值改变(所有其他条件保持不变)。
在我们的示例中,有一对在其中独立更改 a 的值会影响完整决策的值:
id | a | b | c | a && (b || c) |
---|---|---|---|---|
1 | true | true | false | true |
2 | false | true | false | false |
另一对显示,独立更改 b 的值会影响完整决策的值:
id | a | b | c | a && (b || c) |
---|---|---|---|---|
1 | true | true | false | true |
3 | true | false | false | false |
这意味着我们的测试用例证明 a 和 b 独立影响完整决策的价值。就 MC/DC 覆盖而言,它们被覆盖,而 c 条件未被覆盖。
C++test 会报告此示例的 MC/DC 覆盖率为 67%[覆盖 2/3 个条件]。通过将光标置于条件上方,可以在工具提示中查看条件和决策评估的实际值。
简单条件覆盖率
指示所有决策条件的结果覆盖率。决策的总结果数等于 2 * n,其中 n 是决策中的条件数。因此要获得 100% 的覆盖率,所有条件都必须取得所有可能的结果。但是要获得非零覆盖率,一种情况仅需采取一种结果。
每个条件都是一个布尔条件。如果评估为均为真和假,则将其标记为绿色。如果评估为真或假,则将其标记为黄色。如果未评估为真或假,则将其标记为粉红色。
要查看该条件计算的实际布尔值,请将光标置于其上方。该值将显示在工具提示中。
提高覆盖率
在改进测试覆盖率中讨论了提高覆盖率的策略。