`
isiqi
  • 浏览: 16355546 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

防御性编码和单元测试规则(一)

阅读更多

开发人员编写代码。不幸的是,开发人员也编写缺陷,其中大多数缺陷是在最初的编码阶段加入的。修复这些缺陷成本最低的地方同样也是在开发的初始阶段。如果等到功能测试或者系统测试来捕获并修复缺陷,那么您的软件开发成本就会高得多。在本文中,作者 Scott Will、Ted Rivera 和 Adam Tate 讨论了一些基本的“防御性”编码和单元测试实践,让开发人员更容易找到缺陷 —— 更重要的是,从一开始预防缺陷产生。

防御性驾驶和防御性开发

大多数司机接受过防御性驾驶技术的教育 —— 这有很好的理由 —— 但是并不是所有开发人员都接受过防御性开发的教育,特别是那些没有用汇编语言进行过多少开发(如果不是完全没用过的话)、也没有因内存约束和处理器限制而关心过编写极其紧凑的代码的年轻开发人员。本文讨论防御性编码和单元测试概念,它们可以帮助开发人员更快生成更好的代码并且缺陷更少。

为什么防御性开发是重要的?

捕捉错误、问题和缺陷的最佳位置是在开发周期的早期。图 1 展示了最容易出现缺陷的地方,以及最容易发现它们的地方,并包括了修复这些缺陷的成本(这些成本是针对 1996 年的 —— 今天的成本显然更高)。



图 1. 缺陷:引入阶段及发现阶段(包括成本)



当然,比在编码阶段找到缺陷更好的是在一开始就防止它们。防止缺陷应该是开发人员最优先考虑的。我们将分析几个让开发人员可以在编码和单元测试时防止并检测缺陷的简单的、经过证明的方法。

在编译前(防御性设计考虑)

防止缺陷(特别是系统性缺陷)的最有效方式是仔细检查编码所依据的设计。由设计缺陷导致的缺陷 —— 虽然一般不是很多 —— 通常修补成本是最高的。事前花很少的时间针对以下几点对设计进行检查可以得到显著的长期回报。

设计考虑

设计是否有任何不清楚或者混乱的部分?如果是的话,在编写任何代码 之前 澄清这些问题。否则,您可能以一种方式解释一个设计需求,而同事则以另一种方式解释它,从而得到不兼容的实现。

如果您的代码要访问同时被其他组件访问的数据,那么保证您的设计可以处理这种情况。同时,检查设计的安全问题。

如果您的代码严重依赖于其他应用程序的代码,那么您对那个应用程序是否熟悉到可以对设计进行检查?考虑在您的设计检查小组中加入熟悉该产品的一个开发人员。在 设计阶段 发现的集成问题可以得到最有效的处理。

安装和使用考虑

如果您的代码是以前版本的一个升级,那么是否有会使升级失败的参数或者其他选项改变?有哪些其他产品与新代码交互或者集成 —— 如果这些产品本身也改变了呢?还有,您的代码是否容易安装?

操作系统和数据库考虑

您的代码是否会在新版本的操作系统或者数据库上运行?您是否知道这些新版本中加入了哪些改变以及它们是否(及如何)影响您的代码?

这只是测试

设计是否结合了可测试性?虽然您可能认为可测试性问题不是您需要关心的,但是事实上单元测试 是 开发人员的责任之一 —— 几乎所有使执行功能测试和/或系统测试更容易的任何事情也会使单元测试更容易执行。

下面是 可测试性 领域内容的几个例子。

  • 设计是否允许运行时外部工具访问“状态”变量(例如,当前状态),特别是那些测试程序需要用来验证代码是否正确工作以帮助确定问题的变量?

  • 是否对跟踪和日志给予了足够的重视?您让其他人分析缺陷越容易,您在发现缺陷后修正它们就越容易(而且在单元测试中发现自己的问题也会更容易)。

  • 您是否考虑了所有可能调用您的代码的上下文?如果您可以将错误消息与调用它的用户函数上下文相关联,那么用户就更有可能理解这个错误。

  • 设计是否结合了您的测试自动化工具所需要的特定的“钩子(hook)”?

    再多想一想您肯定可以在这个清单中加入更多的内容,特别是那些对您的产品或者组织特定的内容。

    防御性编码技术:编译器是您的朋友

    当您完成对设计的检查后,就轮到编码了。就让我们面对它,除了设计错误外,编码是惟一引入缺陷的地方。无论如何,您的测试程序和客户是不会加入缺陷的 —— 只有 您 会。我们都知道时间很紧张,但是如果您没有时间在第一次就把它编写正确,那么您怎么能找到时间去修正它呢?花上一些时间,这会使您在以后的编码工作中更轻松。

    防止缺陷的最好方法之一是使用编译器。令人恐惧的是,开发人员在编译时通常选择使用最低程度的警告输出,所以请启用编译的全部警告 —— 把即使将编译器配置为检查 所有方面 编译时也不产生一个警告当成编写代码的一个挑战。此外,对代码使用多种编译器使很多程序员获益 —— 这种方法有时会捕获不同的语法错误。

    编码习惯

    下面我们将抛砖引玉介绍几个好的编码习惯。我们不是要为您定义“最佳编码习惯” —— 我们只是要您形成自己遵守的代码编写习惯。下面是几个供参考的最佳习惯的例子。

    在使用前初始化所有变量

    您是否有一组可接受的默认值,特别是对于可能被用户、其他组件或者其他程序有选择地修改的数据?同时,我们强烈要求您列出在最外围例程中要使用的所有本地变量,然后再专门初始化它们。这样不会对您编写代码时的想法留下任何疑问。虽然这可能要多花一些时间并且像是没有理由地增加了几行代码,但是与只是在“运行中(on the fly)”声明本地变量相比,大多数优化编译器不会对此生成任何额外的运行时代码。清单 1 显示了在一个例程中最初几行代码的一个例子:

    清单 1. 初始化本地变量

    <ccid_nobr></ccid_nobr>
    <ccid_code><font size="4"><font color="#a52a2a">public unsigned short TransMogrify( UFEventLink IncomingLink )
      {
        //
        // local variables
        //
        unsigned short usRc;
        String sOurEventType;
        String sTheirEventType;
        //
        // beginning of code
        //
        usRc = 0;
        sOurEventType = null;
        sTheirEventType = null;
        //
        // a miracle occurs...
        //
        return( usRc );
      } // end "TransMogrify"</font></font></ccid_code>


    使用一个“编码标准”文档

    如果您有一个编码标准文档,就使用它。您可以在 Internet 上找到许多种编码标准。找到一种简单的、切中要害、并为您留下一定的活动空间的标准。Sun 的网站有一个关于 Java 编程的编码规范的文章(请参阅 参考资料),它给出了拥有标准的下列几点理由:

    一个软件生存期百分之八十的成本都用在维护上。

    几乎没有软件在整个使用期间都是由原作者维护的。

    编码规范改进了软件的可读性,使工程师可以更快和更充分地理解新代码。

    如果您将源代码作为产品交付,那么需要保证它有像您创建的所有其他产品一样的包装和整洁性。

    即使不赞成“标准”的想法,至少采用这个简单的建议:对变量名使用“匈牙利命名法”,这会使您的代码更容易阅读和维护(。

    保证返回代码的一致性

    在调试时有一种会制造麻烦的情况是:调用程序屏蔽(或者覆盖)一个表示错误的返回代码。一定要想好您要向调用您的代码的例程返回什么值,并保证从您所调用的例程返回的所有错误代码都得到恰当处理。如果返回代码 n 在一个地方意味着一件事,就不要在其他的地方用返回代码 n 表示另一件事。

    对每个例程使用“单点退出”

    这一点怎么强调也不过分:对每个例程使用单点退出 —— 就是说,没有多重返回!这是最容易忽视的、也是您可以采用的最好的习惯。如果例程只从一个地方返回,那么就可以用一种非常容易的方法保证在返回前完成所有必要的清理工作,这也使调试更容易。清单 2 显示了一个包含多重返回的代码示例。注意重复代码、甚至忘记“清理”项目是多么容易。

    清单 2. 单点退出示例

    <ccid_nobr></ccid_nobr>
    <ccid_code><font size="4"><font color="#a52a2a">1   public String getName( )
    2     {
    3       //
    4       // local variables
    5       //
    6       String returnString;
    7
    8
    9       //
    10      // beginning of code
    11      //
    12      returnString = textField.getText( );
    13      if ( null == returnstring )
    14        {
    15          badCount++;
    16          totalCount++;
    17          return( null )
    18        }
    19
    20      returnString = returnString.trim( );
    21      if ( returnString.equals( "" ) )
    22        {
    23          badCount++;
    24          totalCount++;
    25          return( null );
    26        }
    27
    28      totalCount++;
    29      return( returnString );
    30
    31    } // end getName</font></font></ccid_code>


    在第 15 行,badCount 增加了,因为 getText( ) 返回 null。在第 23 行,badCount 代码又重复了。现在想像一下如果这个例子需要完成更复杂的“清理”时会有多混乱。

    清单 3 显示了一种更好的方法:

    清单 3. 单点退出示例 —— 修正后

    <ccid_nobr></ccid_nobr>
    <ccid_code><font size="4"><font color="#a52a2a">1   public String getName( )
    2     {
    3       //
    4       // local variables
    5       //
    6       String returnString;
    7
    8
    9       //
    10      // beginning of code
    11      //
    12      returnString = textField.getText( );
    13      if ( null != returnstring )
    14        {
    15          returnString = returnString.trim( );
    16          if ( returnString.equals( "" ) )
    17            returnString = null;
    18        }
    19
    20      //
    21      // "cleanup"
    22      //
    23      if ( null == returnString )
    24        badCount++;
    25      totalCount++;
    26
    27      return( returnString );
    28
    29    } // end getName</font></font></ccid_code>


    这是一个简化的例子,但是请注意遵照这种习惯有多么容易,以及这样做的好处。
  • 分享到:
    评论

    相关推荐

      企业编码生成系统.rar

      企业编码生成系统是一种用于自动化创建和管理企业内部各种编码的软件解决方案。在现代企业中,编码体系是管理和追踪产品、项目、资产等信息的关键工具,它有助于数据标准化、提高效率和减少错误。本系统通常涉及多个...

      web系统安全和渗透性测试基础

      Web系统安全和渗透性测试是保护在线业务免受恶意攻击的关键环节。在这个领域,了解基本概念、技术以及最佳实践至关重要。下面将详细阐述这个主题。 首先,我们要理解Web系统安全的核心概念。Web系统是由服务器、...

      阿里推荐编码规范

      阿里推荐编码规范是阿里巴巴集团为提升代码质量和团队协作效率而制定的一套标准,它涵盖了多个编程语言和开发领域的最佳实践。这些规范不仅适用于Java、Python、JavaScript等常见编程语言,还包括数据库设计、版本...

      微软一站式代码示例编码规范

      - **正确性**:示例代码需准确展示其意图,经过严格的测试,确保可以按照文档描述成功编译和运行。 - **一致性**:示例代码应遵循统一的编程风格和设计规则,确保不同示例之间具有一致性,方便用户组合使用。 - **...

      2024软件安全测试.doc

      静态代码安全测试(Static Application Security Testing,简称SAST)是一种在软件的编码和设计阶段分析源代码、字节码或二进制文件以识别潜在安全漏洞的技术。这种测试方法不需要运行被测应用程序,可以在软件开发...

      渗透测试笔记.rar

      渗透测试是一种安全评估方法,通过模拟黑客攻击行为来检测系统及网络的安全性。这份名为"渗透测试笔记.rar"的压缩包文件包含了两份文档,分别是"渗透测试-实验拓扑环境 2020年1月22日.pdf"和"渗透测试 2020年1月21日...

      C#语言源代码漏洞测试规范

      测试规范的目的是指导开发人员和测试人员通过一系列流程、规则和技术手段来发现和修补这些漏洞。 规范主要涉及以下几个方面: 1. 输入验证:这是防止注入攻击(比如SQL注入和XML注入)的第一道防线。测试规范需要...

      代码注入器(外挂Call测试的)

      代码注入技术允许开发者将自定义的代码片段插入到运行中的进程,以此来操控或扩展程序的行为,以测试其稳定性和安全性。在游戏领域,外挂Call测试主要是为了检测游戏客户端或服务器是否能够正确处理非法或异常的调用...

      新手软件测试基础知识学习必知

      - 测试类型包括单元测试、集成测试、系统测试、验收测试等,以及白盒、黑盒和灰盒测试。 - 软件问题从错误到缺陷,再到故障和失效,有明确的层次关系。 - 测试标准如GB/T 16260.1和GB/T 18905系列规范指导软件...

      非常实用的软件测试综合资料库

      在一些关键应用 (如民航订票系统、银行结算系统、证券交易系统、自动飞行控制软件、军事防御和核电站安全控制系统等) 中使用质量有问题的软件,还可能造成灾难性的后果。 软件危机曾经是软件界甚至整个计算机界最...

      网络编码协议污点回溯逆向分析方法研究.pdf

      同时,结合编码函数入口地址和编码前内存地址,该方法还能生成符合协议完整性的测试数据,这极大地提升了对编码协议潜在漏洞的发现能力。 总的来说,这篇研究论文提出的网络编码协议污点回溯逆向分析方法,为网络...

      Fortify SCA 代码规则库-支持Java

      在使用Fortify SCA进行代码扫描时,通常会生成一份详尽的报告,列出所有发现的问题,包括问题的严重性、位置和可能的解决方案。开发团队需要根据报告来修复这些问题,以提高软件的安全性。 在"Fortify SCA最新规则...

      Java编程规则

      在开发过程中,重视安全性和防御性编程是必不可少的。这包括验证输入数据、防止SQL注入和XSS攻击、加密敏感信息,以及使用异常处理来增强应用程序的健壮性。安全意识应贯穿于整个开发周期,确保软件的安全性和用户...

      Symbian编码标准

      7. **防御性编程**:提倡采取预防措施以减少错误发生的可能性,比如使用断言和边界检查等技术。 8. **其他规则**:涵盖了一些特定场景下的编程建议,例如国际化支持、性能优化等。 通过上述内容,我们可以看到...

      Chap5 测试与调试1

      防御性程序设计原则旨在减少错误的发生,包括初始化所有变量、编写清晰的注释、避免使用全局变量、对齐大括号以增强可读性、避免假设和重视编译器的警告信息。 在实践中,常见的低级错误主要包括混淆赋值和比较操作...

      WSTG(Web 应用程序安全测试)OWASP - 思维导图.pdf

      2. 配置和部署管理测试:检查服务器配置是否安全,如防火墙规则、更新策略、错误处理机制等。不正确的配置可能导致安全漏洞,使得攻击者能够利用。 3. 搜索引擎侦察:利用搜索引擎查找敏感信息,如内部文档、员工...

      简单的自动注入测试工具WebCruiserEnt

      **自定义规则** 是WebCruiserEnt的一大亮点,这意味着用户可以根据自己的需求或特定项目的安全策略设置个性化的测试规则。这增强了工具的灵活性,使其能适应各种复杂环境,覆盖更广泛的潜在风险。 在实际使用中,...

    Global site tag (gtag.js) - Google Analytics