应用设计模式编写易于单元测试的代码[9] 单元测试方法
与 CacheFactoryImpl 类似地,我们实现了一个 MockCacheFactory,但与 CacheFactoryImpl 不同的是,这个 MockCacheFactory 所创建的 MockCache 对象虽然与真正的 Cache 实现了相同的接口,但是,它的内部实现却是基于 HashMap 的,因此,可以很好地满足单元测试快速、方便地运行的需要。
单元测试时,只需要在 setUp 时调用执行如下操作:
setDelegate(new MockCacheFactory());
将 CacheFactory 的实现委托给 MockCacheFactory 即可,所有业务逻辑都无需作任何修改,因此,这种替换实现的方式几乎是没有侵入性的。
这种通过将实现分离到专门的实现类中的做法其实是 Bridge 模式的一个应用,通过使用 Bridge 模式,为替换实现保留了接口,从而使得在不对业务逻辑作任何修改的情况下可以轻松替换公共服务的实现。
除此之外,Strategy 模式也是一种替换实现的有效途径,这种方式与 Factory Method 类似,通过子类化实现新的 Strategy 以替换业务逻辑使用的旧的 Strategy,通过与 Factory Method 或 Bridge 等模式联合使用,在编写应用公共服务的业务逻辑的单元测试时也十分有用。
绕过部分实现
绕过部分实现进行单元测试在大多数情况下是不可取的,因为这种做法极有可能会影响单元测试的质量。但是对于一些特殊的情况,我们可以“冒险”使用这种方式,比如有这样的一个场景:所有请求需经过多级认证,且部分认证处理需要访问数据库,认证结束后为请求分配相应的 sessionId,请求在获得 sessionId 后继续进行进一步的业务逻辑处理。
在保证多级认证模块已被专门的单元测试覆盖的情况下,我们在为业务逻辑编写单元测试的过程中可以考虑跳过多级认证授权模块(对于部分特权用户,也应跳过部分检查),直接为其分配一个 Mock 的 sessionId,以进行后续处理。软件测试
对于多级认证问题本身,我们可以考虑采用 Chain of Responsibility 模式将不同的认证逻辑封装到不同的 RequestHandler 中,并通过编码或者根据配置,将所有的 Handler 串联成 Responsibility Chain ;而在单元测试过程中,可以修改 Handler 的串联方式,绕过部分不希望在单元测试中经过的 Handler,从而简化单元测试的运行。
对于这个问题,笔者并不同意为了单元测试的需要去采用 Chain of Responsibility 模式,实际上,上面所阐述的多级认证问题本身比较适合采用这种模式来解决,能够根据需要绕过部分实现,只是应用这种模式的情况下进行单元测试的一种可以考虑的测试途径。