Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我能';在不公开私有字段的情况下对类进行单元测试——我的设计有什么问题吗?_Java_Oop_Unit Testing_Dependency Injection_Coding Style - Fatal编程技术网

Java 我能';在不公开私有字段的情况下对类进行单元测试——我的设计有什么问题吗?

Java 我能';在不公开私有字段的情况下对类进行单元测试——我的设计有什么问题吗?,java,oop,unit-testing,dependency-injection,coding-style,Java,Oop,Unit Testing,Dependency Injection,Coding Style,我已经写了一些我认为设计得很好的代码,但后来我开始为它编写单元测试,不再那么确定了 事实证明,为了编写一些合理的单元测试,我需要将一些变量访问修饰符从private更改为default,即公开它们(仅在包内,但仍然…) 下面是我的代码的一些粗略概述。应该有某种地址验证框架,可以通过不同的方式进行地址验证,例如,通过一些外部Web服务或数据库中的数据或任何其他来源进行验证。所以我有一个模块的概念,它就是这样:一种单独的方法来验证地址。我有一个界面: interface Module { pu

我已经写了一些我认为设计得很好的代码,但后来我开始为它编写单元测试,不再那么确定了

事实证明,为了编写一些合理的单元测试,我需要将一些变量访问修饰符从
private
更改为
default
,即公开它们(仅在包内,但仍然…)

下面是我的代码的一些粗略概述。应该有某种地址验证框架,可以通过不同的方式进行地址验证,例如,通过一些外部Web服务或数据库中的数据或任何其他来源进行验证。所以我有一个
模块的概念,它就是这样:一种单独的方法来验证地址。我有一个界面:

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;
  }

}
然后,我编写了一个
模块
,它使用一些外部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;
   }

}
所以基本上我有这个
webservicecfacade
,它是外部web服务的包装器,我的模块调用这个facade,处理它的响应并返回一些框架标准响应

我想测试
webservicecmodule
是否正确处理来自外部web服务的响应。显然,我不能在单元测试中调用真正的web服务,所以我在模仿它。但是,为了让模块使用我的模拟web服务,必须从外部访问字段
webservice
。它打破了我的设计,我想知道我是否能对此做些什么。显然,不能在init参数中传递facade,因为
ModuleFactory
不知道也不应该知道需要它

我已经读到依赖注入可能是这些问题的答案,但我不知道如何解决?我以前没有使用过任何DI框架,所以我不知道在这种情况下是否可以轻松使用它。但也许可以

或者我应该改变我的设计

或者干脆把这个不幸的字段包变成私有的(但是留下一个悲伤的注释,比如
//默认可见性以允许测试(哦,好吧…
),感觉不太对)


呸!在写这篇文章时,我突然想到,我可以创建一个
webserviceceprocessor
,它将
webservicecade
作为构造函数参数,然后只测试
webserviceceprocessor
。这将是解决我问题的方法之一。你觉得怎么样?我有一个问题,因为我的
webservicecmodule
会有点无用,只是把它的所有工作委托给另一个组件,我会说:一层抽象太远了。

是的,你的设计是错误的。您应该在类中执行而不是
new…
(也称为“硬编码依赖项”)。无法轻松编写测试是错误设计的完美指示器(请阅读范例中的“倾听您的测试”)


顺便说一句,在这种情况下,使用反射或破坏依赖关系的框架是一种非常糟糕的做法,应该是您的最后手段。

是的,您的设计是错误的。您应该在类中执行而不是
new…
(也称为“硬编码依赖项”)。无法轻松编写测试是错误设计的完美指示器(请阅读范例中的“倾听您的测试”)


顺便说一句,在这种情况下,使用反射或破坏依赖关系的框架是一种非常糟糕的做法,应该是您的最后手段。

是的,您的设计是错误的。您应该在类中执行而不是
new…
(也称为“硬编码依赖项”)。无法轻松编写测试是错误设计的完美指示器(请阅读范例中的“倾听您的测试”)


顺便说一句,在这种情况下,使用反射或破坏依赖关系的框架是一种非常糟糕的做法,应该是您的最后手段。

是的,您的设计是错误的。您应该在类中执行而不是
new…
(也称为“硬编码依赖项”)。无法轻松编写测试是错误设计的完美指示器(请阅读范例中的“倾听您的测试”)


顺便说一句,在这种情况下,使用反射或破坏依赖性的框架是一种非常糟糕的做法,应该是您的最后手段。

我同意yegor256所说的,并想指出,您最终陷入这种情况的原因是您为模块分配了多重责任:创建和验证。这违反了标准,有效地限制了您单独测试创建和验证的能力

考虑将“模块”的责任仅限于创建。当他们仅承担此责任时,命名也可以改进:

interface ValidatorFactory {
  public Validator createValidator(InitParams params);
}
验证界面变得独立:

interface Validator {
  public ValidationResponse validate(Address address); 
}
然后,您可以从实现工厂开始:

class WebServiceValidatorFactory implements ValidatorFactory {
  public Validator createValidator(InitParams params) {
    return new WebServiceValidator(new ProdWebServiceFacade(createParamsForFacade(params)));
  }
}
这个工厂代码很难进行单元测试,因为它显式地引用了prod代码,所以请保持这个impl非常简洁。将任何逻辑(如
createParamsForFacade
)放在旁边,以便可以单独测试它

web服务验证器本身只负责验证,并将外观视为一个依赖项,遵循以下原则:

class WebServiceValidator implements Validator {
  private final WebServiceFacade facade;

  public WebServiceValidator(WebServiceFacade facade) {
    this.facade = facade;
  }

  public ValidationResponse validate(Address address) {
    WebService wsResponse = webservice.validate(address);
    ValidationResponse reponse = proccessWsResponse(wsResponse);
    return response;
  }
}
由于
WebServiceValidator
不再控制其依赖项的创建,测试变得轻而易举:

@Test
public void aTest() {
   WebServiceValidator validator = new WebServiceValidator(new MockWebServiceFacade());
   ...
}
通过这种方式,您有效地反转了对依赖项创建的控制:控制反转(IoC)

哦,顺便说一下,先写你的测试。这样,您自然会倾向于可测试的解决方案,这通常也是最好的设计。我认为这是因为测试需要模块化,而模块化恰好是良好设计的标志。

我同意