应用设计模式编写易于单元测试的代码[8] 单元测试方法
替换实现
通过 Factory Method 替换被创建对象可以满足一些修改程序运行路径的需求,但是,这种方法以子类化为前提,具有很强的侵入性,并且在编写单元测试时,开发人员需要同时负责 Mock Objects 的开发,供 Factory Method 调用,因此,编码量往往会比较大,单元测试开发人员也需对所使用的公共模块的内部结构有十分清楚的认识。即使可以使用公共的 Mock Objects 实现避免代码重复,往往也需要修改业务逻辑中公共服务相关对象的创建代码,这一点对于应用公共模块的业务逻辑的单元测试可能不太适合。
在笔者曾参与设计、开发的某应用系统中,有一个专门的数据库缓冲(Cache)公共服务,该 Cache 负责完成与数据库交互,实现数据的存取,并缓存数据以提高后续访问的效率。对于涉及数据库缓冲的业务逻辑的单元测试,需要一个替代方案来替代已有的数据库缓冲,以避免直接访问实际数据库,但又要保证这个替换不会影响到被测试单元的实现。
为了解决这个问题,我们并没有直接替换 Cache 创建处的代码,因为这些代码遍布在业务代码中,直接替换 Cache 创建代码无疑会侵入业务逻辑,并需要大量使用子类化。为了尽可能降低对业务逻辑的影响,我们维持了原有 CacheFactory 的接口,但是将 CacheFactory 的实现委托(Delegate)给另一个实现类完成,以下是 CacheFactory 实现的伪代码: 软件测试
package com.cachefactory.demo;
public abstract class CacheFactory {
private static CacheFactoryinstance = new DelegateCacheFactory();
private static CacheFactorydelegate;
protected CacheFactory() {
}
// CacheFactory is a singletonpublic
static CacheFactory getInstance() {
return instance;
}
// the implementation can be changedprotected
static void setDelegate(CacheFactory instance) {
delegate= instance;
}
public abstract Cache getCache(Object... args);
// redirect all request to delegateeprivate
static class DelegateCacheFactoryextendsCacheFactory {
private DelegateCacheFactory() {
}
public Cache getCache(Object... args) {
return delegate.getCache(args);
}
}
}