单体测试指南

1、单体测试应该小并且快

  理想情况下在每次代码签入之前都要执行下测试套件。测试快就可以降低开发周转时间。

  2、单体测试必须完全自动化,不需要交互

  测试套件通常经常执行,必须完全自动化才有用。如果结果需要人工检查,该测试就不是真正的单体测试。

  3、单体测试应便于运行

  开发环境应该配置成通过一个单独的命令或者一个按钮点击就可以运行单独的测试和测试套件。

  4、评估测试

  对测试的运行进行覆盖分析从而得到执行覆盖并调查代码的哪些部分执行,哪些部分没有执行。

  5、立刻修复失败测试

  每个开发者都应该确保在签入后新的测试以及所有既有的测试都可以成功运行。

  如果在日常测试执行中有一个测试失败了,整个团队必须停下手头的工作来保证问题得到解决。

  6、保持测试在单体层

  单体测试和类的测试相关。每个普通类都要有一个测试类,类的行为应该在隔离的条件下测试。应该避免使用单体测试框架测试整个工作流程,因为这样的测试会很慢并很难维护。会有需要流程测试的地方,但不应该作为单体测试的一部分,它必须独立地设置和执行。

  7、从简单开始

  一个简单的测试要比根本没有测试好。一个简单的测试类可以建立起目标类的测试框架并可以验证编译环境、单体测试环境、执行环境和覆盖分析工具是否具备以及是否正确,从而可以证明目标类是否是程序集的一部分以及是否可以访问。

  单体测试的入门程序可能像这样:

void testDefaultConstruction()
{
    Foo foo = new Foo();
    assertNotNull(foo);
}

  8、保存测试相互独立

  为了确保测试健壮并简化维护,测试不能依赖其它测试以及测试执行的先后顺序。

  9、测试类和被测试类尽量近

  如果被测试类是Foo,那么测试类就应该命名为FooTest(而不是TestFoo)并同Foo放在同一个包里面。将测试类放在单独的目录下会使其难于访问和维护。

  确保编译环境的配置可以使得测试类不会进入生产库或执行文件中。

  10、合理命名测试

  确保每个测试方法测试一个明显的类功能并据此命名测试方法。典型的命名规则是test[what],比如testSaveAs(), testAddListener(), testDeleteProperty()等。

  11、测试公开API

   单体测试被定义为通过公开API测试类。有些测试工具可以实现类的私有方法的测试,但由于这会使得测试太过繁琐并更难于维护因此需要避免。如果有一些类 的私有方法需要显示地进行测试,考虑将其重构成工具类的公开方法。要这样做就应该要改进总体设计,而不是仅仅为了帮助测试。

  12、以黑盒考虑

  作为第三方的类使用者来测试该类是否满足需求。尝试着让它失效。

13、以白盒测试考虑

  毕竟,开发者写测试的同时也写了被测试类,需要特别注意测试复杂逻辑。

  14、微不足道的类也要测试

  有人会推荐测试所有主要的情况,而可以忽略诸如简单的类似setter和getter等微不足道方法。 然而,应该测试微不足道方法情况有几个原因:

  ● 很难定义微不足道。对不同的人可能有不同的含义。

  ● 从黑盒测试的角度看无法知道代码的哪部分是微不足道的。

  ● 由于拷贝-粘贴操作,微不足道的代码也可能包含错误。

private double weight_;
private double x_, y_;
public void setWeight(int weight)
{
   weight = weight_;    // 错误
}
public double getX()
{
   return x_;
}
public double getY()
{
   return x_;    // 错误
}

  建议就是测试所有代码,毕竟微不足道的代码很容易测试。

  15、首先关注执行覆盖率

  执行覆盖不同于实际代码覆盖。一个测试的最初目标应该保证高的执行覆盖。这可以确保代码在某些参数下真正执行。有了这个,就可以去改善测试覆盖了。注意实际代码覆盖很难确定(通常都很接近0%)。

  考虑下面这个公开方法:

void setLength(double length);

  通过调用setLength(1.0) 你就可能得到100% 的执行覆盖率。为了达到真正100%的实际测试覆盖,必须使用所有可能的double值来调用该方法以确认它们的正确行为。这显然是不可能的。

  16、覆盖边界情况

  确保覆盖参数边界的情况。对于数,测试负数、0、正数、最小、最大、NaN、无穷等情况。对于字符串,考虑空字符串、单个字母的字符串、非 ASCII的字符串、多字节的字符串等情况。对于集合,测试空集合、单个元素集合、第一个、最后一个等。对于日期,考虑1月1日、2月29日、12月31 日等。被测类会提示各个具体情况下的边界情况。由于这些都可能是错误的根源,因此要尽可能多地测试这些情况。

  17、提供随机数生成器

  在覆盖了边界情况后,进一步提高覆盖率的一个简单方法就是产生随机数以使得每次测试都可以使用不同的输入执行。

  为了实现这个目标,可以提供一个生成double、integer、 string和dates等类型随机数的实用类。生成器必须可以从各个类型的全范围内生成值。

  如果测试很快,可以考虑在一个循环内运行尽可能多的组合。下面的例子就是验证通过一次大端和一次小端转化是否可以得到原值。由于测试很快,每次可以根据不同的值执行100万次。

void testByteSwapper() 

    for (int i = 0; i < 1000000; i++) 
    { 
          double v0 = Random.getDouble(); 
          double v1 = ByteSwapper.swap(v0); 
          double v2 = ByteSwapper.swap(v1); 
          assertEquals(v0, v2); 
    } 
}

 18、每次测一个功能

  在测试模式下,很容易去尝试对每个测试中的每件事都断言。这必须避免因为它使得维护更难。仅测试该测试方法的名字表明的功能。

  对于一般代码应该将测试代码尽可能少作为一个目标。

  19、使用显式断言

  在assertEquals(a, b)和assertTrue(a == b) (之类的)之间优先选择前者,因为它可以在测试失败的时候给出关于失败原因的更多有用的信息。 这对于上述提到的随机参数组合时输入值无法预先知道的情况尤为重要。

  20、提供负面测试

  负面测试是通过故意用错代码以验证稳健性和适当的错误处理。

  考虑下面这个如果传入负数就会抛出异常的方法:

void setLength(double length) throws IllegalArgumentException;

  可以这样来测试该特殊情况的正确行为:

try

     setLength(-1.0); 
     fail();    // If we get here, something went wrong 

catch (IllegalArgumentException exception)

     // If we get here, all is fine 
}

  21、设计代码是考虑测试

  编写和维护单体测试的代价很高,使公开API最小化并降低代码的循环复杂度是降低成本并使得高覆盖率的测试代码更快编写和更易于维护的方式。

  一些建议:

  ● 通过构造时候确定状态来使得成员类不可变。这会减少对setter方法的需求。

  ● 限制过渡使用继承和虚的公开方法。

  ● 减少利用了友元类(C++)或包范围(Java)的公开API。

  ● 避免不必要的分支。

  ● 在分支内的代码尽可能少。

  ● 尽可能使用异常和断言来验证分别在公开和私有API中的参数。

  ● 限制使用帮助方法。从一个黑盒测试的角度每个方法都必须一样地测试。考虑如下这个小例子:

public void scale(double x0,double y0,double scaleFactor) 

      // scaling logic 

 
public void scale(double x0, double y0) 
 
      scale(x0, y0, 1.0); 
}

  省去后面的就可以简化测试,但代价是客户端代码会有额外的工作。

 22、不要关联到预定义的外部资源

  编写测试文件的时候应该不能利用将要执行环境的上下文信息以使得他们可以在任何时间任何地方运行。为了给测试提供所需的资源,这些资源应该通过测试自身提供。

  现在考虑解析某种类型文件的类的情况。应该将文件的内容放在测试内,在测试开始前将其写入一个临时文件并在测试完成后删除掉文件,而不是从预定义的路径选择一个示例文件。

  23、了解测试成本

  不写单体测试代价很高,写单体测试代价也很高。这是两者之间的平衡,从执行覆盖率考虑一般的行业标准是大约是80%。

  通常比较困难达到完全覆盖的区域是处理外部资源的错误或异常。在一个交易中模拟数据库崩溃是完全有可能的,但较之作为替代方法的深度代码检查通常代价过高。

  24、测试优先级排序

  单体测试通常是一个至底而上的流程,如果没有测试系统所有部分所需的足够资源就应该优先考虑最底层。

  25、考虑到测试代码失败

  考虑下这个简单的实例:


Handle handle = manager.getHandle();
assertNotNull(handle);

String handleName = handle.getName();
assertEquals(handleName, "handle-01");

  如果第一个断言是假的,接下来的语句就会崩溃,余下的测试也就不会被执行了。所以应该考虑单个测试代码失效不会使得这个测试套件无法执行。通常可以改写成这样:


Handle handle = manager.getHandle();
assertNotNull(handle);
if (handle == null) return;

String handleName = handle.getName();
assertEquals(handleName, "handle-01");

  26、编写测试来重现缺陷

  如果报告了一个缺陷,就应该写一个测试来重现该缺陷(比如一个失败的测试)并使用该测试作为是否成功修复代码的标准。

  27、认识局限性

  单体测试永远不能证明代码的正确性!

  一个失败的测试意味着代码含有错误,而一个成功的测试不能证明任何东西。

  单体测试的最普遍的应用就是验证和记录底层的需求以及回归测试:验证代码在变迁和重构的过程中一直保持稳定。

  因此,单体测试永远无法替代前期设计和健全的开发流程。单体测试可以作为既有开发方法的一个宝贵补充。

  后记:

  对于第9点”测试类和被测试类尽量近”和第14点”微不足道的类也要测试”,我不是很赞同,实践的时候也没有完全遵守。但为了忠实于原文没有做 擅自修改。大家在学习和实践的时候可能也会接触一些相悖的观点和理论,不要担心,选择最适合你尝试下就知道了。同时,欢迎说出你的故事!

====================================分割线================================

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-10-21 08:05:37

单体测试指南的相关文章

F#单体测试

最近试着写了些F#代码.不过习惯了TDD的我心里感觉有点不踏实,是不是还缺点什么呢?对了,单体测试.经过一番搜索和调查我决定试试 FsUnit.结合该框架的示例代码的学习备注如下. (* 添加引用 *) #r "FsUnit.NUnit.dll" #r "nunit.framework.dll" (* 导入命名空间 *) open NUnit.Framework open FsUnit (* 定义被测类 *) type LightBulb(state) = memb

NSA DanderSpiritz测试指南——木马生成与测试

本文讲的是NSA DanderSpiritz测试指南--木马生成与测试, 0x00 前言 DanderSpritz是NSA的一款界面化的远控工具,基于FuzzBunch框架,执行Start.jar即可启动. 在实际测试过程中,由于缺少说明文档,遇到的问题有很多,同时一些细节也值得深入研究.所以本文将要帮助大家答疑解惑,分享测试心得,结合木马特点分析防御思路. 0x01 简介 本文将要介绍以下内容: 执行pc_prep无法获得回显的原因及解决方法 Pc同Pc2.2的区别 level3和level4

渗透测试指南之域用户组的范围

本文讲的是渗透测试指南之域用户组的范围,一直以来,我对Active Directory组的范围总是有点搞不清楚.对于任何从事过AD系统管理员背景的人来说,这个话题可能并不是很难,但是直到我阅读了SS64的这篇文章后,关于AD组范围的所有东西我都搞明白了.我想记录一些关于这个话题的相关说明(我所知道的),以防其他任何人都有同样的困惑.我还将介绍这些组范围如何与林全局目录和域信任进行交互,并在此过程中加入新的PowerView功能. Active Directory组 Active Director

《Nmap渗透测试指南》目录—导读

内容提要 Nmap渗透测试指南 本书专门介绍Nmap渗透测试的有关内容,全书共分12章,从最基础的Nmap下载.安装开始介绍,由浅入深地对Nmap的功能作了完整详细的说明.同时书中还包括了大量的实践案例,更有利于读者对Nmap使用的理解.本书主要内容包括:Nmap基础.Nmap工作原理.扫描指定段.Nmap主机发现.TCP ACK Ping扫描.ARP Ping扫描.路由跟踪.探索网络.从Nmap识别端口状态.隐蔽扫描.指纹识别与探测.重量级扫描.调整探测报文的并行度.防火墙/IDS逃逸.源端口

《Nmap渗透测试指南》—第1章1.1节Nmap介绍

第1章 Nmap基础学习 Nmap渗透测试指南 本章知识点 Nmap介绍 Windows下安装Nmap Linux/Unix源码编译安装Nmap Linux通过RPM软件包安装Nmap Mac OS安装Nmap Nmap语法 Nmap全面扫描 Nmap扫描指定段 本章节将介绍在几大主流平台中如何安装Nmap,并介绍多种安装方式,通过对每一步的演示进行解说,让初学者可以很快地掌握安装Nmap技巧,以及如何简单地使用Nmap扫描一个目标地址.一个IP段,从而迈入Nmap渗透测试的大门. 本章选项 1

《Nmap渗透测试指南》—第9章9.节审计HTTP身份验证

第9章 渗透测试 Nmap渗透测试指南 本章知识点 审计HTTP身份验证 审计FTP服务器 审计Wordpress程序 审计Joomla程序 审计邮件服务器 审计SMB口令 审计VNC服务器 审计SMTP服务器 检测Stuxnet蠕虫 SnMP安全审计 本章节将介绍Nmap脚本对主流CMS程序.网络服务.网络病毒的检测,本章更是引入了对工控系统病毒Stuxnet检测技术.切勿将本章内容用于非法用途,请遵守相应的道德标准. 本章脚本 表9.1所示为本章节所需Nmap命令表,为方便读者查阅,笔者特此

当研究PCI渗透测试指南时,你应该注意这六个方面

尽管PCI DSS 3.0版本已经全面推出,但仍然有很多关于企业难以遵守11.3章节中列出的PCI渗透测试要求的讨论. 为了帮助企业充分了解PCI DSS 3.0要求,PCI安全标准委员会在2015年3月发布了PCI DSS补充信息:渗透测试指南.该文档详细介绍了渗透测试过程的一般方法,从范围界定到测试不同的网络层,再到测试后续步骤(例如报告)等. PCI渗透测试文档以及PCI DSS合规所要求的方法的优点是,并没有什么新东西.除了提到云计算环境.网络钓鱼以及缩小持卡人数据环境范围等新概念外,这

《Nmap渗透测试指南》—第10章10.1节Zenmap应用

第10章 Zenmap应用Nmap渗透测试指南本章知识点 Zenmap介绍Zenmap基本配置Zenmap扫描模板Ports/Hosts标签Topology标签Host Details标签Scans标签编辑扫描模板新建扫描模板本章节将介绍Zenmap的使用.作为Nmap初学者最好的帮助工具,Zenmap以其强大的操控界面可以完成很多在Shell终端下复杂的命令.Zenmap包含了不同的模板,我们可以对这些模板进行自定义设置,让它更加灵活易用. 10.1 Zenmap介绍Zenmap是经典安全扫描

《Nmap渗透测试指南》—第7章7.1节信息搜集

第7章 信息搜集Nmap渗透测试指南本章知识点 信息搜集IP信息搜集WHOIS查询搜集E-mail信息IP反查DNS信息搜集检索系统信息后台打印机服务漏洞系统漏洞扫描扫描Web漏洞通过Snmp列举Windows服务/账户枚举DNS服务器的主机名HTTP信息搜集枚举SSL密钥SSH服务密钥信息探测本章节将介绍Nmap的NES脚本,脚本是用Lua程序创作的,目前已有好几百种.lua的易用性也让更多的用户加入脚本的创作当中来,本章节通过对Nmap信息搜集脚本的使用让大家了解Nmap的高级技法. 本章脚