使用静态工厂方法创建的对象的模拟行为



我正在重构一个 Hibernate 映射对象Gadget以删除getIntFieldValuesetIntFieldValue,并更改我的代码以从 DAO 对象中检索该值,该对象是使用 Factory 创建的,并将小工具传递给该对象。

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) {
    //...
}

DoerClass看起来像这样

Session dbSessn;
Gadget gadget;
public DoerClass(Session dbSessn, Gadget gadget) {
    this.dbSessn = dbSessn;
    this.gadget = gadget;
}
public List<Result> produceResults() {
    //this is to be replaced
    int intFieldValue = this.gadget.getIntFieldValue()
    //with
    //GadgetPropertyDAO gadgPropDAO = GadgetPropertyAccessFactory.getGadgetPropertyDAO(this.dbSessn);
    //int intFieldValue = gadgPropDAO.getDeviceIntFieldValue(this.gadget);

    //generate List<Result> based on intFieldValue
}

我的问题是,在我能够方便地模拟getIntFieldValue将在produceResults返回的内容之前,但现在我使用的是静态返回的 DAO,我不知道是否可以模拟GadgetPropertyDAO.getDeviceIntFieldValue(this.gadget)将返回的内容。

是否可以在不更改我的方法签名 (API) 的情况下进行模拟?

我同意 Tom G 的观点:Mockito 和依赖注入(可以说是 Java 本身)实际上是为实例设计的,而不是静态方法——这是利用 Java 多态优势的唯一方法。如果切换到将工厂设置为实例,它将如下所示:

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;
// ...

另一种选择是忍受和管理您的测试差距:

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);
  // ...
}

。然后,您可以使用模拟来测试produceResultsInternal,这会让您获得 80% 的悲伤测试。

相关内容

  • 没有找到相关文章

最新更新