我已经编写了一些我认为设计得非常好的代码,但后来我开始为它编写单元测试并且不再那么确定了。
事实证明,为了编写一些合理的单元测试,我需要将我的一些变量访问修饰符从private
更改为default
,即公开它们(仅在包内,但仍然......)。
这是我有问题的代码的一些粗略概述。应该有某种地址验证框架,可以通过不同的方式进行地址验证,例如通过某些外部 Web 服务或通过数据库中的数据或任何其他来源来验证它们。所以我有一个概念Module
,就是这样:一种验证地址的单独方法。我有一个界面:
interface Module {
public void init(InitParams params);
public ValidationResponse validate(Address address);
}
有某种工厂,它根据请求或会话状态选择适当的模块:
class ModuleFactory {
Module selectModule(HttpRequest request) {
Module module = chooseModule(request);// analyze request and choose a module
module.init(createInitParams(request)); // init module
return module;
}
}
然后,我编写了一个Module
使用一些外部 web 服务进行验证的程序,并像这样实现它:
WebServiceModule {
private WebServiceFacade webservice;
public void init(InitParams params) {
webservice = new WebServiceFacade(createParamsForFacade(params));
}
public ValidationResponse validate(Address address) {
WebService wsResponse = webservice.validate(address);
ValidationResponse reponse = proccessWsResponse(wsResponse);
return response;
}
}
所以基本上我有这个WebServiceFacade
,它是外部 Web 服务的包装器,我的模块调用这个外观,处理它的响应并返回一些框架标准的响应。
我想测试WebServiceModule
进程是否正确地从外部 Web 服务响应。显然,我不能在单元测试中调用真正的 Web 服务,所以我在嘲笑它。但话又说回来,为了让模块使用我的模拟 Web 服务,该字段webservice
必须可以从外部访问。它破坏了我的设计,我想知道我是否可以做些什么。显然,facade 不能传入 init 参数,因为ModuleFactory
不知道也不应该知道需要它。
我已经读过依赖注入可能是解决此类问题的方法,但我不知道怎么做?我之前没有使用过任何 DI 框架,比如Guice,所以我不知道在这种情况下是否可以轻松使用它。但也许可以?
或者也许我应该改变我的设计?
或者把它搞砸并将这个不幸的现场包设为私有(但留下一个感觉// default visibility to allow testing (oh well...)
不对的悲伤评论)?
呸! 在我写这篇文章的时候,我突然想到,我可以创建一个WebServiceProcessor
将 aWebServiceFacade
作为构造函数参数的,然后只测试WebServiceProcessor
. 这将是我的问题的解决方案之一。你怎么看待这件事?我对此有一个问题,因为那样我WebServiceModule
会有点无用,只是将其所有工作委托给另一个组件,我会说:一层抽象太远了。