一、单测中的问题

  从代码来看,大家测试的意识比较强,现有代码已有部分单元测试和系统测试case;从case的质量来看,基本符合标准的组织和书写形式,集成测试大家都会注意测试数据的准备和清理。但也存在一些问题,主要表现为:

  1、测试代码的风格不一致,维护成本会比较高。有些用JdbcTemplate直接写Sql插入准备数据,有些用Dbunit来导入数据;有些在service采用集成测试,有些采用单元测试;

  2、生产代码的可测性不强,导致测试用例很难写全面。现在在service层大多数接口方法依赖于多个private方法,如何某个private方法中有重要逻辑,不能直接进入逻辑中测试;

  3、测试方法不得当,导致书写用例的成本很高。现在的用例普遍采用service层集成测试的方式,由于业务层逻辑关系比较复杂,大多数情况需要构造多张数据库表的数据才能测试,构造数据过程耗时耗力。同时由于集成测试都在spring的容器中,每个case初始化时间都比较长,导致调试和运行都不方便。

  二、单测整体解决方案

  为了提高单测的质量和效率,需要从生产代码和测试代码两方面入手。一方面代码的可测性高低与系统设计的优劣是一对孪生兄弟,如果单元测试易于开展则系统一定符合高内聚、低耦合的特性。另一方面测试代码需要符合一定的规范和实施原则,以保证用例能够低成本开发和调试。

  为解决以上问题,首先需要对已有不利于测试的代码进行重构以保证单测顺利开展,这里主要在遵循面向对象设计原则的前提下,通过分析业务逻辑关系,采用适宜的设计模式或者调整代码来解决。

  其次需要统一测试的方法:service层进行单元测试,dao层进行集成测试。原因如下,service层逻辑一般涉及较多的业务关系,需要细粒度的测试,如果与dao混在一起测试,构造数据不方便,容易导致测试不充分;dao层现在采用ibatis框架,有较多原生sql语句,为验证sql的正确性必须采用集成测试,同时对单一dao进行测试时一般只关联其对应的bo,测试成本也不高。

  三、重构与单测实例

  以下主要是这次重构改进可测性的一些典型实例,后续代码书写和单测参考可作为参考,为了屏蔽复杂业务逻辑带来的干扰,这里主要展示伪代码:

  3.1 门面模式的运用(Facade)

  先看一下原来的类:

  我们可以看到这个类有一个handle方法,主要分为两步完成它的功能,第一步从自己的dao中查出一个map然后根据业务逻辑进行加工,第二步将map中的整型值通过调用barService替换成字符串。实际代码中当然没有这么简单,比较常见的形式的FooServiceImpl中用到了2-3Dao和2-3个Service才能完成handle()应该实现的功能。下面我们来看如何对handle()方法进行单元测试: