我已经编写了一些我认为设计得很好的代码,但后来我开始为其编写单元测试并不再那么确定。
事实证明,为了编写一些合理的单元测试,我需要更改一些变量访问修饰符 private
至default
,即公开它们(仅在包内,但仍然......)。
这是我的相关代码的一些粗略概述。应该有某种地址验证框架,可以通过不同的方式进行地址验证,例如通过一些外部网络服务或数据库中的数据或任何其他来源来验证它们。所以我有一个Module
的概念,这就是:一种验证地址的单独方法。我有一个界面:
interface Module {
public void init(InitParams params);
public ValidationResponse validate(Address address);
}
有某种工厂,它根据请求或 session 状态选择适当的模块:
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
它使用一些外部网络服务进行验证,并像这样实现它:
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服务,所以我在 mock 它。但话又说回来,为了让模块使用我模拟的 Web 服务,字段 webservice
必须可以从外部访问。它破坏了我的设计,我想知道我是否可以对此做些什么。显然,facade 不能传入 init 参数,因为ModuleFactory
不知道也不应该知道它是必要的。
我读到依赖注入(inject)可能是此类问题的答案,但我不知道如何解决?我之前没有使用过任何DI框架,比如Guice ,所以我不知道在这种情况下是否可以轻松使用它。但也许可以?
或者也许我应该改变我的设计?
或者搞砸了,把这个不幸的现场包设为私有(private)(但留下像 // default visibility to allow testing (oh well...)
这样的悲伤评论感觉不对)?
呸!当我写这篇文章时,我突然想到,我可以创建一个 WebServiceProcessor
这需要 WebServiceFacade
作为构造函数参数,然后仅测试 WebServiceProcessor
。这将是我的问题的解决方案之一。你怎么看待这件事?我对此有一个问题,因为那时我的 WebServiceModule
只是将所有工作委托(delegate)给另一个组件,这有点无用,我想说:一层抽象太远了。
最佳答案
是的,你的设计是错误的。你应该这样做dependency injection而不是类中的 new ...
(也称为“硬编码依赖项”)。无法轻松编写测试是错误设计的完美指标(请阅读Growing Object-Oriented Software Guided by Tests中的“聆听您的测试”范例)。
顺便说一句,使用反射或依赖打破框架,如 PowerMock在这种情况下这是一个非常糟糕的做法,应该是您最后的选择。
关于java - 我无法在不暴露私有(private)字段的情况下对我的类进行单元测试——我的设计有问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19310817/