摘要:如果没写单元测试,如若在branch中对之前代码重构的话,则没有移回trunck上的勇气,有了单元测试,全部运行通过后则有信心合并。互联网公司更是需要重视单元测试,因为版本迭代比较迅速。因此一个好的单元测试框架及一个好的项目质量管理非常重要。本文即是我对这些的心得体会。
  关键词:java, 单元测试, TestNG, DbUnit, Spring, 项目管理, 质量管理, PMP
  解决问题:单元测试该如何实施?项目质量管理该如何执行?
  在开发数据访问对象DAO层时,我们需要直接对数据层进行增删改查CRUD操作。单元测试非常重要,因为在开发期间经常需要进行代码重构,怎样才能保证代码重构的正确性呢,怎样给代码重构者以信心让他放手去做呢,需要执行单元测试,只要能保证接口功能不发生任何变化,与代码重构前完全相同,并且能直观的感受到这一一致性,开发者便能大胆地去干了。功能开发容易,单元测试难做。第一,单元测试间相互影响:单元测试1对数据A新增的一条数据可能会影响到单元测试2验证的正确性,你可能会想到在每个单元测试的起始时手动做一遍初始化,比如清理一遍表中数据,以清除其他单元测试的影响,这样笨且增加工作量。第二,开发者间相互影响:如果有多人同时需要执行测试用例,使用以上的办法后还是会发生问题,这时我们可能会选择每个开发者自己搭建一套数据库(内存或大型),以避免多人间的干扰,这样还是过于麻烦,且消耗资源。
  下面是基于TestNG和DbUnit的单元测试框架,它的基本思想是管理事务,在单元测试起始时使用事务,在单元测试逻辑的后将事务回滚,因此每个单元测试内对数据库的操作将不会实际对数据库有实质性影响,这样在单元测试中既可以测试逻辑的正确性,又避免影响到了其他单元测试和其他开发者,并且只需要依赖于统一的开发数据库即可,使用还很方便。以下还使用MyBatis的功能抽象出了一个统一平台,该统一平台提供了大多数的公共接口,如增删改查及批量操作等,大部分的基础操作可以通过调用这些接口能完成,不通用的操作传入SQL语句也可执行。
  一、MyBatis统一平台:MyBatisAngelWang.class

  @Repository
  publicclassMyBatisAngelWangimplementsIRepository{
  @Autowired
  privateGeneralDAO generalDAO;
  public<T extendsBase> T get(Class<T> clz,Long id){
  HashMap hashMap = generalDAO.getLogically(clz, id);
  T ret =this.convert(hashMap, clz);
  return ret;
  }
  }

  上面的代码即对数据库直接进行了操作,我们需要对此接口编写单元测试。具体的MyBatis使用方法,及MyBatisAngelWang统一平台的实现办法,需要另抽专门章节进行详细讨论。在这里不做更深入研究了。
  二、统一平台的单元测试:MyBatisAngelWangTest

  import org.testng.annotations.Test;
  //@DatabaseSetup(value= "/dbunitData/TestAngelEntity.xml")
  publicclassMyBatisAngelWangTestextendsAbstractRollbackTest{
  @Autowired
  privateMyBatisAngelWang myBatisAngelWang;
  @Test(enabled =false)
  publicvoid testGet(){
  }
  }

  可以看到此单元测试MyBatisAngelWangTest.class继承自一个抽象类:AbstractRollbackTest。

  import org.springframework.test.context.ContextConfiguration;
  import org.springframework.test.context.TestExecutionListeners;
  @ContextConfiguration(locations ={"classpath:spring-datasource-dbunit.xml",
  "classpath*:spring-services.xml"})
  @TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
  TransactionDbUnitTestExecutionListener.class,TransactionalTestExecutionListener.class})
  @Transactional
  publicclassAbstractRollbackTestextendsAbstractTestNGSpringContextTests{
  }

  此抽象类是由我们自己定义的,其继承自抽象类:AbstractTestNGSpringContextTests,它由Springframework提供。我们可以通过ContextConfiguration注解来注入spring配置文件。
  或者这样也可以。单元测试MyBatisAngelWangTest.class直接继承自AbstractTestNGSpringContextTests。减少了一层。
  @ContextConfiguration("/config/Spring-db.xml")
  @Transactional
  @ActiveProfiles("test")
  publicclassMyBatisAngelWangTestextends
  AbstractTransactionalTestNGSpringContextTests{}