楠族开心果 发表于 2010-6-28 09:23:32

如何编写干净的单元测试用例

如大家所见,我们在这里定义了setupEnv()和cleanEnv()两个方法,分别用于初始化环境和清除测试数据,然后在测试方法开始和结束时分别调用这两个方法。这的确达到了我们的目的,不用在每个测试方法中都编写初始化和清除逻辑!但此时你一定发现在每个测试方法前后都调用setupEnv()和cleanEnv()也很不爽,那说明我们的抽象程度还不够!那么该如何做的更好呢?
      这里该到模板方法(Template Method)模式发挥威力的时候了。我们将使用模板方法来继续重构前面的案例。让我们先来定义一个方法:

/**
* @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(){
//do test logic in this method
execute();
}
protected void execute(){
setupEnv();
doTestLogic();
setupEnv();
}
}
      相比之前的方法,我们这里已经有了一些进步,定义了一个execute方法,在该方法开始和结束分别执行初始化和清除逻辑,然后由doTestLogic()方法实现测试逻辑。实际测试方法中只要执行execute方法,并传入测试逻辑就可以了。瞧,不经意间我们已经实现了模板方法模式——把通用的逻辑封转起来,变化的部分由具体方法提供。怎么,不相信么?呵呵,设计模式其实并不复杂,就是前人解决通用问题的一些最佳实践总结而已。

      此时你可能会说,TeseCase类已经提供了setUp()和tearDown()方法来做这件事情,我也想到了,哈哈!但这并不和本文产生冲突!
   
      问题似乎越来越清晰,但我们遭遇了一条无法跨越的鸿沟——如何才能把测试逻辑传递到execute方法中呢?单靠传统的编程方法已经无法解决这个问题,因此我们必须寻找其他途径。

      可能此时此刻你已经想到,本文另一个重要概念——回调方法模式还没有用到,是不是该使用该模式了?没错,就是它了!我先把代码给出,然后再详细解释。

      我们提供了一个抽象类TestExecutor,并定义一个抽象的execute方法,然后为测试类的execute方法传入一个TestExecutor的实例,并调用该实例的execute方法。最后,我们的测试方法中只需要new一个TestExecutor,并在execute方法中实现测试逻辑,便可以按照预期的方式执行:准备测试环境-执行测试逻辑-清除测试数据。这便是一个典型的回调方法模式的应用!

      模板方法和回调函数模式说起来挺悬,其实也就这么简单,明白了吧:)

    三、如何为每个测试方法单独提供环境方法呢?

      通过前面的讲解,相信大家对模板方法和回调函数模式都已经掌握了,这里直接给出相关代码:

/**
* 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;
configLocations = "classpath:godzilla/dal/godzilla-db-test.xml";
configLocations = "classpath:godzilla/dal/godzilla-dao.xml";
for (int i = 2; i < configLocations.length; i++) {
configLocations = customConfigLocations;
}
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;
}
/**
* 准备测试环境.
*/
protected void setupEnv() {
}
/**
* 清除测试数据.
*/
protected void cleanEvn() {
}
/**
* 测试用例执行器.
*/
protected abstract class TestExecutor {
/**
* 准备测试环境
*/
public void setupEnv() {
}
/**
* 执行测试用例.
*/
public abstract void execute();
/**
* 清除测试数据.
*/
public void cleanEnv() {
}
}
/**
* 执行一个测试用例.
*
* @param executor
*/
protected final void execute(final TestExecutor executor) {
execute(IgnoralType.NONE, executor);
}
/**

kuangquanshui 发表于 2010-6-28 11:01:14

这是开心果吗?    真的是开心果    你发的是什么呀我都看不懂 也不给讲讲

Jackc 发表于 2010-6-28 11:30:23

坐等详解,最好开心果能给个工具的安装包,俺们也能实战一把::ybaojc:::

cncnily 发表于 2010-7-15 15:07:07

自动化测试?
页: [1]
查看完整版本: 如何编写干净的单元测试用例