Java 使用静态工厂方法创建的对象的模拟行为
我正在重构一个Hibernate映射对象Java 使用静态工厂方法创建的对象的模拟行为,java,mockito,Java,Mockito,我正在重构一个Hibernate映射对象Gadget以删除getIntFieldValue和setIntFieldValue,并更改代码以从DAO对象检索该值,DAO对象是使用工厂创建的,并向其传递了一个Gadget public class GadgetPropertyAccessFactory { public static GadgetPropertyDAO getGadgetPropertyDAO(Session dbSessn){ if(getSomeBoo
Gadget
以删除getIntFieldValue
和setIntFieldValue
,并更改代码以从DAO对象检索该值,DAO对象是使用工厂创建的,并向其传递了一个Gadget
public class GadgetPropertyAccessFactory {
public static GadgetPropertyDAO getGadgetPropertyDAO(Session dbSessn){
if(getSomeBooleanFromDB(dbSessn)) {
return new TrueImplGadgetPropertyDAO();
} else {
return new FalseImplGadgetPropertyDAO();
}
}
...
测试代码如下所示:
//this mocks a Gadget
Gadget gadget = createGadget();
//this is to be replaced
when(gadget.getIntFieldValue()).thenReturn(2);
DoerClass doerClass = new DoerClass(null, gadget);
List<Result> doerResults = doerClass.produceResults();
for (Result doerResult : doerResults) {
//...
}
public class GadgetPropertyAccessFactory {
public GadgetPropertyDAO getGadgetPropertyDAO(Session dbSessn){
if(getSomeBooleanFromDB(dbSessn)) {
return new TrueImplGadgetPropertyDAO();
} else {
return new FalseImplGadgetPropertyDAO();
}
} // ...
}
public class DoerClass {
Gadget gadget;
Session dbSessn;
// Sets default implementation. Constructor injection would also work.
GadgetPropertyAccessFactory gpaFactory = new GadgetPropertyAccessFactory();
public DoerClass(Session dbSessn, Gadget gadget) {
this.dbSessn = dbSessn;
this.gadget = gadget;
}
public List<Result> produceResults() {
GadgetPropertyDAO gadgPropDAO =
gpaFactory.getGadgetPropertyDAO(this.dbSessn);
int intFieldValue = gadgPropDAO.getDeviceIntFieldValue(this.gadget);
// ...
}
}
// in your test
DoerClass doerClass = new DoerClass(null, gadget);
GadgetPropertyAccessFactory mockFactory =
Mockito.mock(GadgetPropertyAccessFactory.class);
doerClass.gpaFactory = mockFactory;
// ...
我的问题是,在我能够方便地模拟getIntFieldValue
将在produceResults
中返回的内容之前,但现在我使用的是静态返回的DAO,我不知道是否可以模拟GadgetPropertyDAO.getDeviceInFieldValue(this.gadget)
将返回的内容
在不改变我的方法签名(API)的情况下,模拟是可能的吗?我同意Tom G的观点:模拟和依赖注入(可以说是Java本身)实际上是为实例设计的,而不是为静态方法设计的,这是利用Java多态性优势的唯一方法。如果您切换到将工厂作为实例,它将如下所示:
//this mocks a Gadget
Gadget gadget = createGadget();
//this is to be replaced
when(gadget.getIntFieldValue()).thenReturn(2);
DoerClass doerClass = new DoerClass(null, gadget);
List<Result> doerResults = doerClass.produceResults();
for (Result doerResult : doerResults) {
//...
}
public class GadgetPropertyAccessFactory {
public GadgetPropertyDAO getGadgetPropertyDAO(Session dbSessn){
if(getSomeBooleanFromDB(dbSessn)) {
return new TrueImplGadgetPropertyDAO();
} else {
return new FalseImplGadgetPropertyDAO();
}
} // ...
}
public class DoerClass {
Gadget gadget;
Session dbSessn;
// Sets default implementation. Constructor injection would also work.
GadgetPropertyAccessFactory gpaFactory = new GadgetPropertyAccessFactory();
public DoerClass(Session dbSessn, Gadget gadget) {
this.dbSessn = dbSessn;
this.gadget = gadget;
}
public List<Result> produceResults() {
GadgetPropertyDAO gadgPropDAO =
gpaFactory.getGadgetPropertyDAO(this.dbSessn);
int intFieldValue = gadgPropDAO.getDeviceIntFieldValue(this.gadget);
// ...
}
}
// in your test
DoerClass doerClass = new DoerClass(null, gadget);
GadgetPropertyAccessFactory mockFactory =
Mockito.mock(GadgetPropertyAccessFactory.class);
doerClass.gpaFactory = mockFactory;
// ...
公共类GadgetPropertyAccessFactory{
公共GadgetPropertyDAO getGadgetPropertyDAO(会话dbSessn){
if(getSomeBooleanFromDB(dbessn)){
返回新的TrueImplGadgetPropertyDAO();
}否则{
返回新的FalseImpledGadgetPropertyDao();
}
} // ...
}
公共类DoerClass{
小玩意;
会话dbSessn;
//设置默认实现。构造函数注入也可以工作。
GadgetPropertyAccessFactory gpaFactory=新的GadgetPropertyAccessFactory();
public DoerClass(会话dbessn,Gadget Gadget){
this.dbSessn=dbSessn;
this.gadget=gadget;
}
公开列表生产结果(){
GadgetPropertyDAO gadgPropDAO=
gpaFactory.getGadgetPropertyDAO(this.dbessn);
int intFieldValue=gadgPropDAO.getDeviceIntFieldValue(this.gadget);
// ...
}
}
//在你的测试中
DoerClass DoerClass=新DoerClass(空,小工具);
GadgetPropertyAccessFactory模拟工厂=
Mockito.mock(GadgetPropertyAccessFactory.class);
doerClass.gpaFactory=模拟工厂;
// ...
另一种选择是接受并管理您的测试差距:
public List<Result> produceResults() {
return produceResultsInternal(gpaFactory.getGadgetPropertyDAO(this.dbSessn));
}
/** Visible only for testing. Do not call outside of tests. */
List<Result> produceResultsInternal(GadgetPropertyDAO gadgPropDAO) {
int intFieldValue = gadgPropDAO.getDeviceIntFieldValue(this.gadget);
// ...
}
公共列表produceResults(){
返回ProduceResultInternal(gpaFactory.getGadgetPropertyDAO(this.dbessn));
}
/**仅在测试时可见。不要在测试之外调用*/
列出产品结果内部(GadgetPropertyDAO gadgPropDAO){
int intFieldValue=gadgPropDAO.getDeviceIntFieldValue(this.gadget);
// ...
}
…然后,您可以使用模拟来测试内部的produceResultsInternal
,这会让您在20%的痛苦中得到80%的测试。听起来您的设计在倒退——您的DoerClass
不应该负责从DAO检索小工具。它不是在检索小工具。小工具通过构造函数传递给它。produceResults需要数据库中其他地方的重构字段,这些字段现在通过分解的DAOMy MIRST访问——不过,像这样检索DAO是这里的问题。我的建议是采用更具依赖注入风格的方法,并提供GadgetPropertyDAO
作为构造函数参数。然后可以模拟getDeviceIntFieldValue
调用。尝试将PowerMock与Mockito结合使用。我认为您可以模拟GadgetPropertyAccessFactory.getGadgetPropertyDAO(this.dbessn)
并返回一个模拟的GadgetPropertyDAO
。有关详细信息,请选中此项: