单元测试时使用技术

  单元测试,我们一般使用一些非入侵式的单元测试框架,比如JUnit/NUnit/Microsoft Unit Test Framework等。虽然他们的名字和技术不同,但基本的思想还是类似的。做为设计和开发人员,可以从如下几个方面考虑单元测试:如何识别测试单元?如何组织测试单元?如何隔离高成本资源?单元测试环境与运行环境如何切换的解决方案是什么?单元测试与持续集成方案。以上每个问题都可以独立成文,甚至独立成书,本文只做简单介绍,希望能起到抛砖引玉的作用。

  如何识别测试单元?

  一个可测试的单元可能是一个类或一个函数、方法。在面向对象的开发中,我们往往将一个类做为一个测试单元。当然,一个由很多静态方法组成的工具类应该是每个方法是一个可测试单元。当然,我们必须遵守面向对设计的基本原则,比如单一职责原则(SRP, Single Responsibility Principle),依赖倒置原则(DIP, Dependency Inversion Principle),接口隔离原则(ISP, Interface Segregation Principle),Liskov替换原则(LSP, Liskov Substitution Principle)等。

  另外,我们做单元测试时,应该明确我们测试的范围是我们自己的代码,不要考虑自己代码范围之外的事情。我们要假设其他代码都是正确的。比如.NET framework、JVM、Hibernate等等,还有被测试代码依赖的工具类、接口实现的代码,他们都不在我们的测试范围内。这个道理象我们量身高时,假设尺是准确的,我们并不是用我们的身高检测尺子。

  如何组织测试单元?

  一般的面向对象的单元测试框架基本的单元测试组织是测试类。测试类都可以定义这个测试类的初始化方法(setupBeforeClass),测试类的清理方法(tearDownClass),测试方法的初始化方法(setup),测试方法的清理方法(tearDown)(不同框架用的函数名可能不同,也有可能只是用attribute或announce标识)。对于这个测试类来说,只需准备一次的代码,写在setupBeforeClass中,终的清理代码写在tearDownClass中,每个函数都需要重新准备的代码写在setup中,清理代码写在tearDown中。写在一个测试类中的代码要可以共享setup和tearDown方法中的代码,如果不能共享,应该写在两个测试类中。如果需要多个测试类共享setupBeforeClass方法和tearDownClass方法,可以使用继承机制。对于测试类非常多的项目,可以将互相影响、依赖很大的测试类组成一个测试suite(在MS Unit Test Framework中是一个配置文件)。

  如何隔离高成本资源?

  对于这个问题,解决方案都是一个答案了,是坚持接口隔离原则(ISP)和依赖倒置原则(DIP)。单元测试时,使用mockup framework或其他技术或手工创建模拟对象来模拟依赖对象的真实运行情况。

  需要注意的问题是,这部分的工作可能比较枯燥,而且需要细心,在接口不稳定的情况下,修改的工作量可能比较大,估算时要留出必要的工时,设计时要注意这部分代码的重用。我们可以在设计阶段定义一个标准的模拟环境,让正常流程的测试代码共享一个标准模拟环境。

  单元测试环境与运行环境如何切换的解决方案是什么?

  单元测试中,在setupBeforeClass方法或setup方法中决定并创建测试类依赖的接口的实现对象。在正式代码中使用工厂或springframework等框架解决依赖问题。

  单元测试与持续集成方案

  如果项目中使用了单元测试,为了能使这些代码能发挥大的作用,持续集成是一个不错的方法。

  每种技术都用对应的持续集成方案,比如Microsoft Team Foundation Server,CruiseControl, CruiseControl.NET,Hudson等,虽然他们功能的强弱不同,但是他们的目的都很明确,定时或触发(有新代码commit)时下载新代码、编译、执行单元测试、发送结果。中间还可以使用一些代码检查工具做一些代码检查比如checkstyle、findbugs、fortify、FXCop等。

  其他一些需要注意的问题

  1、避免debug单元测试代码,尽量将日志记录的好一些,当在持续集成环境中发现问题时可以容易的定位

  2、架构师或开发人员要先写出标准代码(在大团队进入项目前,如果是小团队,可以先结对开发几个到两个用例),供其他开发人员参考,开发人员在第一周写出的单元测试代码必须通过评审,以后的代码要抽查

  3、如果没有强制性要求,不建议对展现层直接做单元测试,因为使用代码对焦点获得效果等检查还是比较困难的。可以将展现层与控制层分离,对控制层做单元测试,而将展现层的测试留给测试人员或QTP等自动化测试工具执行

  4、测试覆盖度只是且仅仅只是告诉你,那个部分已经被测试了;并不能告诉你,你的测试写的是正确的

  5、再说一次执行速度。测试代码一定要快,别让开发人员厌烦