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

时间:2014-02-17 19:10:36

标签: java mockito

我正在重构一个Hibernate映射对象Gadget以删除getIntFieldValuesetIntFieldValue并更改我的代码以从DAO对象中检索该值,该对象是使用{{1}创建的和小工具通过。

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中返回的内容之前,但现在我正在使用静态返回的DAO,我不知道是否可以模拟produceResults将返回。

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

1 个答案:

答案 0 :(得分:0)

我同意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%的测试中获得20%的悲伤。