getConfigLocations()方法为AbstractDependencyInjectionSpringContextTests 提供配置,Spring会根据该配置文件自动注入bizUrlDAO属性。testInsert()方法用于测试插入新数据,注意这里有个问题,如果数据库中已经存在该URL的记录,则应用会报错,所以这里还要进行数据清除准备处理,我们称之为“测试环境准备”,以后会用到该名词;testDuplicateInsert()方法用于测试插入重复数据的情况,该方法同样存在上面的问题;testDelete()方法用于测试删除数据的情况,这里尽管准备了数据,但仍没有考虑数据库中已经有记录的情况。
综上所述,尽管该测试类已经比较清晰,但仍然存在许多不足之处。我们将在后面的章节进行详细分析,并给出解决方案。
Callback Function & Template Method Pattern
回调函数(Callback Function)和模板方法(Template Method)是软件架构设计中最常用的两种设计模式,这两种设计模式在Spring框架中随处可见。
关于本节是否要详细介绍回调函数(Callback Function)和模板方法(Template Method)模式的问题,笔者考虑了很长时间。因为网络上对这两种普遍使用的设计模式的定义层出不穷,各有各的道理,很难说谁是谁非。况且,针对不同的应用场景,这两种模式也有许多变体,或者联合使用。
因此,笔者最终决定不在此处对这两种模式做任何定义或引用,请读者自行参阅相关文档资料。
回调函数和模板方法模式在单元测试中的应用
上一节我们简单的回顾了回调函数和模板方法模式,Spring框架中大量采用了这两种设计模式,有兴趣的读者可以阅读Spring框架代码进一步巩固对这两种模式的理解和运用。本节将结合回调函数模式和模板方法模式对前面的测试用例进行重构,读者可以在重构过程中逐步了解这两种设计模式的运用。
首先,让我们简单总结一下前面测试用例的问题:
一、抽象层次太低,不够通用?
例如,对于getConfigLocations()方法,我们完全可以放到一个父类中实现,因为对于一个项目而言,其配置文件大多都是统一的,没有必要在没有测试类中都定义该方法。
/**
* DAL层测试支持类.
*
*
* 除非特殊情况,所有DAO都要继承此类.
*
* @author tao.youzt
*/
public abstract class GodzillaDalTestSupport extends AbstractDependencyInjectionSpringContextTests {
/*
* @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations()
*/
@Override
protected final String[] getConfigLocations() {
String[] configLocations = null;
String[] customConfigLocations = getCustomConfigLocations();
if (customConfigLocations != null && customConfigLocations.length > 0) {
configLocations = new String[customConfigLocations.length + 2];
configLocations[0] = "classpath:godzilla/dal/godzilla-db-test.xml";
configLocations[1] = "classpath:godzilla/dal/godzilla-dao.xml";
for (int i = 2; i < configLocations.length; i++) {
configLocations[i] = customConfigLocations[i - 2];
}
return configLocations;
} else {
return new String[] { "classpath:godzilla/dal/godzilla-db-test.xml",
"classpath:godzilla/dal/godzilla-dao.xml" };
}
}
/**
* 子类可以覆盖该方法加载个性化配置.
*
* @return
*/
protected String[] getCustomConfigLocations() {
return null;
}
}
如图所示,我们提炼了一个抽象支持类,实现了getConfigLocations()方法,同时还提供了getCustomConfigLocations()方法供子类使用,子类可以通过重载该方法提供定制的配置。
有了该支持类,具体测试类只需要继承该类并编写测试逻辑即可。
二、缺少准备测试环境和清除测试数据的环节?
对于大多数测试用例,可能都会涉及到初始化数据和清除测试数据的问题,最典型的就是数据库操作,这也是本文采用数据库操作作为案例的原因。那么如何实现呢?很显然在每个测试方法中都编写准备环境和清除测试数据的代码是不合适的,因为大多数时候对于一个测试类而言,准备环境和清除数据的逻辑都是一样的。聪明的你一定会想到定义两个方法,一个初始化环境,一个清除测试数据。是的,就是这样!
/**
* @author tao.youzt
*/
public class TestBizUrlDAO extends AbstractDependencyInjectionSpringContextTests {
private BizUrlDAO bizUrlDAO;
@Override
protected String[] getConfigLocations() {
return new String[]{"godzilla-dao.xml","godzilla-db.xml"};
}
protected void setupEnv(){
bizUrlDAO.delete("www.easyjf.com");
}
protected void cleanEnv(){
bizUrlDAO.delete("www.easyjf.com");
}
public void testTemp(){
setupEnv();
bizUrlDAO.insert(generateDO());
assertNotNull(bizUrlDAO.getByUrl("www.easyjf.com"));
setupEnv();
}
}