同一个 bug 不要修复两次
admin
2023-07-31 00:48:15
0

Noah Sussman 曾经写过一篇文章 《你应该测试的东西:软件系统测试清单》这份清单里面大部分东西都是有帮助的。然而我觉得它所鼓励的理念,本质上来说有误。

它的理念基本上是这样:找出开发者常犯的错误,然后确保你写了测试样例来检查你没有犯了这样的错误。

然而这个做法的问题是它本质上是一种“打地鼠”式的调试方式,并没有终结掉那些该死的 bug。

一个更有效的做法是《Easy Programming》里提倡的“永远不要重复解决同一个 bug”(在这篇文章大约三分之一处)。

如果你遇到了一个或者一类经常出现的 bug,你不应该首先想到的就是“最好把它们加到我需要测试的这类 bug 的列表里面”,你应该想的是“我应该如何修改我的系统,使得这类 bug 再也不会出现?”

所以,对下面列出的一些东西进行测试:

  • 输入处理的 bug,比如 SQL 注入或者 XSS 攻击在 Django 应用里,我从来不为 SQL 注入或 XSS 攻击写测试,原因就是对于 Django 应用来说这类 bug 是非常罕见的。因为用来执行 SQL 语句和生成 HTML 的 API 很容易使用,并且默认是打开了安全机制的。如果我的程序出现了任何这类的 bug,
    那真是奇怪而又难以理解。用黑盒测试这种机枪法来找 bug 是在浪费时间。对于 XSS 来说,我有几次都有点想用一些容易遭受 XSS 攻击的方式来生成 HTML 代码。

    比如说,用 Python 的字符串插入。然后,在这个时候,你仍然不应该通过测试来保证你正确地写好了它,相反你应该马上停止写愚蠢的 HTML 生成代码。

    为了这么做,你首先得问一下你自己“为什么”,“为什么我会想用这种方式来写 HTML”。答案一般是“没有一个 API 能方便地用来实现我想要的这个功能”。正确的做法现在显而易见:创造一个新的 API,移除掉那些易引入潜在的漏洞的代码。

    所以,这里有一些我在一个项目里写的一些代码,通过字符串插入来实现的一个格式化超链接的模板标签:

    123456789101112131415 from django.core.urlresolvers import reverse   from django.template import Library   from django.utils.html import escape   from django.utils.safestring import mark_safe    register = Library()    @register.filter   def account_link(account):       return mark_safe(u\'%s\’ % (                escape(reverse(\’account_stats\’, args=(account.username,))),                escape(account.first_name),                escape(account.last_name),                escape(account.username),                ))

    这样做的一个问题是我必须得记得为每一个变量进行转义。我这么写的原因是 Django 的模板 API 在这种使用情况下会很笨重。所以,我应该这么写:

    123456789101112131415 from django.core.urlresolvers import reversefrom django.template import Library from somewhere import html_fragment register = Library() @register.filterdef account_link(account):    return html_fragment(u\'%s\’,            reverse(\’account_stats\’, args=(account.username,)),            account.first_name,            account.last_name,            account.username,            )

    然后写这个 API:html_fragment, 这样写就可以了:

    12345 from django.utils.html import escapefrom django.utils.safestring import mark_safe, conditional_escape def html_fragment(template, *args):    return mark_safe(template % tuple(map(conditional_escape, args)))

    编辑:在 django-devs 上讨论过后以及后来做的修改,这个已经包含在 Django 1.5 中了,用’django.utils.html.format_html’ 而不是上面的那些代码。

    现在我不再用那种容易遭到攻击的方式写代码了,所以我也不需要测试了(虽然我可能想要为 html_fragment 做一两个测试)。现在有两种方式来做这件事——对于小的 html 片段用 html_fragment, 更大的就用 Django 的模板 API。两者默认都是安全的。

    所以,如果你发现你需要为你代码中可能存在的特殊的 SQL 注入或者 XSS 攻击做测试的话,你可能正在做一件错误的事情。修复可能出错的底层的 API 应该放在首位。

  • 输入类型检查 -“非法的值比如 null 和 NaN, 应该为字符串而不是整数,应该为数组而不是字符串”输入的处理应一般只发生在信任的边界,但它应该在那个边界上系统的发生。如果你因为传递错误的类型的值给内部的函数而造成了问题的话,因为正在传递额外的输入,你不应该在函数里面处理它,你应该问“这怎么可能发生呢?”

    一个解决办法是使用静态类型系统。当然这可能在你的环境下并不可行,但是你也应该考虑一下它。

  • 数学相关的 bug:在 Noah 的文章中列出的许多例子,其根本的 bug 都是因为编程语言做了一些滑稽的事情所以你应该问“为什么我要用这种语言?它是做好这件工作的正确工具吗?”

    或者至少问一下“是否已经有一些现成的库可以解决这个问题?”

    如果你要处理高精度数,解决方法就是用一个成熟的不容易出错的库。比如,Python 的 decimal 库不允许浮点数和高精度数相乘。这听起来可能有点不方便,但是它杜绝了一切可能出 bug 的可能。

  • 单位度量如果你有跟这相关的潜在的 bug 的话,你应该问“这怎么可能发生呢?我怎么做才能完全消除这些 bug 呢?”

    不同的编程语言有不同的解决方法,这经常与把度量的单位也作为值的一部分有关。比如 Haskell 有很多方案会使得“3年”可能会与“2米” 相加,而编译器会在编译的时候停下来。

    对于 Python,有像 magnitude 和 units 这样的东西使得在运行的时候正常工作。

    当然,如果你只是在内部使用这些的话,你可能仍有一些需要对输入做转换等边界情况,在这种情况下你可能需要知道正在输入的内容的单位,但是:

    1. 你应该尝试坚持让外部的系统传递带单位信息的数值,所以你不必依赖常识来判断,如果某些事情改变了你立即就能感知到。而且你内部的 API 可以通过强制这个问题来帮你做这个。
    2. 即使外部的系统不能被改变,你仍然可以有极少数个地方可以检查,就是你的输入和输出的边界情况,每一个都应该单独处理。通过写测试样例并不能帮你太多,你最多只要写一个就够了,作为一个单独的集成测试。你的时间应该花在检查那些边界情况上。
  • 文字——处理 unicode 的输入和 ASCII 输入有不同吗?同样的,你应该使用正确的内部数据类型—— 在任何地方都用 unicode ——为了消除潜在的 bug。

    Python 2.x 在字符串上声名狼藉——纵容字节串和 unicode 之间的转换在生产而不是开发环境中产生了无穷无尽的 bug。

    正确的解决方法是写测试样例——可能你现在就得做了——而不是解决底层的问题,它已经在 Python3 中被解决了。

  • 同样的东西可以应用到 Noah 的列表里其他几乎所有的事情上,如果我不能看出怎么应用的话,我就会想一定是我的想象力不够 :-)。如果我不能想出一个完全解决一类 bug 的办法的话,我应该更加努力的想,而不是假设它是不可能发生的。

    注意这是一个老程序员的观点,这个列表里遗漏了一些有名的东西——比如缓冲区溢出。想必是作者使用的语言或框架不可能产生这样的 bug。之所以这样不是因为前一代的程序员写了大量的测试,而是因为他们写的语言和系统不可能或者几乎不可能产生这类 bug。

    所以,我在这里要说的是你甚至不必为这些 bug 写测试。你每次找到一类这样的 bug 时,测试总是显得多么苍白无力。你应该确保你绝不会解决同一个 bug 两次,所以也绝不会写两次同样的测试。如果你正在写本质上同样的测试超过了一次,那证明你还并没有解决真正的问题。

    如果一个 bug 值得添加到常见的 bug 列表中,于是你就发现了一个你的平台上的系统问题,它值得完全消除。我们应该追求那些这样做的库/编程语言/系统。

    相关内容

    热门资讯

    Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
    500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
    定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
    scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
    65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
    小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
    pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
    微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
    Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
    python清除字符串里非数字... 本文实例讲述了python清除字符串里非数字字符的方法。分享给大家供大家参考。具体如下: impor...