02代码覆盖率

上一章中,我们介绍了基本的模糊测试,即生成随机输入来测试程序。我们如何衡量这些测试的有效性?一种方法是检查发现的错误的数量(和严重性);但是,如果 bug 很少,我们需要一个代理来衡量测试发现 bug 的可能性。在本章中,我们将介绍代码覆盖率的概念,它衡量在测试运行期间实际执行程序的哪些部分。对于试图覆盖尽可能多的代码的测试生成器来说,测量这种覆盖率也至关重要。

这段代码定义了一个名为 cgi_decode 的函数,它用于解码CGI(Common Gateway Interface)编码的字符串。CGI编码通常用于URL编码,以便在URL中安全地传输特殊字符。函数接收一个字符串作为输入,并返回解码后的字符串。

函数内部实现了以下功能:

  1. 定义hex_values字典:该字典用于将十六进制字符('0'-'9', 'a'-'f', 'A'-'F')映射到它们对应的十进制整数值。

  2. 解码处理:函数通过遍历输入字符串的每一个字符来进行解码。

    • 如果遇到加号('+'),则将其替换为一个空格字符。
    • 如果遇到百分号('%'),则检查其后的两个字符是否都是有效的十六进制字符。如果是,则计算这两个十六进制字符对应的十进制值,并将其转换为对应的字符。然后将这个字符添加到解码后的字符串中。如果百分号后的两个字符不是有效的十六进制字符,则抛出一个ValueError异常,表示无效的编码。
    • 如果遇到其他字符,则直接将其添加到解码后的字符串中。
  3. 返回解码后的字符串:当遍历完输入字符串的所有字符后,函数返回解码后的字符串。

这个函数在处理URL或其他需要编码的文本时非常有用,因为它能够正确地将CGI编码的字符串转换回原始的、可读的字符串。
---------------------------------------------------------------------------------------------------------------------------------

以下是工作原理的示例:cgi_decode()

cgi_decode("Hello+world")
'Hello world'

如果我们想系统地测试,我们将如何进行?cgi_decode()

测试文献区分了两种推导测试的方法:黑盒测试白盒测试

黑盒测试的优点是它可以在指定行为中发现错误。它独立于给定的实现,因此允许在实现之前创建测试。缺点是,实现的行为通常比指定的行为涵盖更多的领域,因此仅基于规范的测试通常不能涵盖所有实现细节。

白盒测试

与黑盒测试相比,白盒测试实现中派生测试,尤其是内部结构。白盒测试与覆盖代码结构特征的概念密切相关。例如,如果代码中的某个语句在测试期间未执行,则意味着该语句中的错误也无法触发。因此,白盒测试引入了许多覆盖标准,在测试可以说是充分的之前,必须满足这些标准。最常用的承保标准是

  • 语句覆盖率 – 代码中的每个语句必须由至少一个测试输入执行。
  • 分支覆盖率 – 代码中的每个分支必须由至少一个测试输入获取。(这意味着每个和决定一次是真的,一次是假的。ifwhile

除此之外,还有更多的覆盖率标准,包括所采用的分支序列、所采用的循环迭代(零、一、多)、变量定义和用法之间的数据流等等;[Pezzè et al, 2008] 有一个很好的概述。

白盒测试的优点是它可以发现已实现行为中的错误。即使在规范没有提供足够细节的情况下,也可以进行;实际上,它有助于识别(从而指定)规范中的极端情况。缺点是它可能会错过未实现的行为:如果缺少某些指定的功能,白盒测试将找不到它。

跟踪执行

白盒测试的一个很好的功能是,人们实际上可以自动评估是否涵盖了某些程序功能。为此,可以检测程序的执行,以便在执行过程中,一个特殊的功能跟踪执行了哪些代码。测试后,这些信息可以传递给程序员,然后程序员可以专注于编写涵盖尚未发现的代码的测试。

在大多数编程语言中,设置程序以便可以跟踪其执行是相当困难的。在 Python 中并非如此。该函数允许定义一个跟踪函数该函数为执行的每一行调用。更好的是,它可以访问当前函数及其名称、当前变量内容等。因此,它是动态分析的理想工具,即分析执行过程中实际发生的情况。

这些代码主要用于追踪Python程序的执行过程,特别是cgi_decode函数的执行。通过使用sys.settrace()函数和自定义的追踪函数traceit,我们可以收集到cgi_decode函数执行过程中访问的每一行代码的行号,并将这些行号存储在全局变量coverage中。

以下是代码的简要介绍:

  1. 定义追踪函数 traceit
    • 接收三个参数:frame(当前执行帧的引用)、event(追踪事件类型)和arg(与事件相关的参数)。
    • 当事件为'line'(即代码中的一行被执行)时,将当前执行的函数名和行号添加到全局列表coverage中。
    • 错误地返回了traceit函数自身,这实际上会导致无限递归(应该返回None)。
  2. 定义包装函数 cgi_decode_traced
    • 这个函数接受一个字符串s作为参数,并用于调用cgi_decode函数,但在调用过程中进行追踪。
    • 在调用cgi_decode之前,重置coverage列表并开启追踪(通过sys.settrace(traceit))。
    • 在调用cgi_decode之后,关闭追踪(通过sys.settrace(None))。
  3. 测试追踪
    • 使用cgi_decode_traced("a+b")调用包装函数,并传入字符串"a+b"作为参数。
    • 追踪过程中收集到的行号被存储在coverage列表中。
    • 打印coverage列表,以显示cgi_decode函数执行时访问的行号。

实际上,这些是哪一行?为此,我们获取源代码并将其编码成一个数组,然后我们将用覆盖率信息对其进行注释。省略中间代码,目的为了在源代码中显示出那些代码没有被执行覆盖。

---------------------------------------------------------------------------------------------------------------------------------

Coverage

上面的内容描述了一个使用Python的with语句来管理资源(在这种情况下是代码覆盖率追踪)的概念。with语句是Python中用于确保代码块执行完毕后进行清理操作(如关闭文件、释放锁等)的上下文管理协议的一部分。这个协议定义了两个方法:__enter____exit__,它们分别在进入和退出with语句块时自动调用。

这里的关键点在于,你描述了一个名为Coverage的类,这个类可能用于追踪代码覆盖率。这个类实现了__enter____exit__方法,以便在with语句块开始时启动追踪,并在块结束时停止追踪。同时,该类可能还提供了一个方法(例如coverage)来获取追踪结果。

Coverage类的具体代码实现省略,上图为使用覆盖类的代码。

多次进行随机输入的覆盖率,中间代码省略,画图如下图所示。

---------------------------------------------------------------------------------------------------------------------------------

C语言程序可以实现同样的内容,代码见书。

在笔记本执行的时候改了两行代码,因为不是linux系统,所以原有的两行代码无法执行。

有趣的是,我们之前设计的所有手动测试都不会触发这个错误。实际上,无论是声明还是分支覆盖率,以及文献中通常讨论的任何覆盖率标准都找不到它。但是,简单的模糊测试运行可以通过几次运行来识别错误 - 如果适当的运行时检查已到位以发现此类溢出。这肯定需要更多的模糊测试!

经验 教训

  • 覆盖率指标是一种简单且完全自动化的方法,用于估算在测试运行期间实际执行的程序功能量。
  • 存在许多覆盖率指标,其中最重要的是报表覆盖率和分支机构覆盖率。
  • 在 Python 中,在执行过程中非常容易访问程序状态,包括当前执行的代码。

后续步骤

覆盖率不仅是衡量测试有效性的工具,也是指导测试生成实现特定目标(尤其是未覆盖代码)的绝佳工具。我们使用 coverage 来

---------------------------------------------------------------------------------------------------------------------------------

练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值