如何模拟通过Class.newInstance(className)创建的对象?

时间:2016-02-12 16:00:42

标签: java unit-testing mockito

我正在尝试将单元测试添加到传递给它的String类名称的遗留代码中,并使用Class.newInstance(String className)创建实现特定处理程序接口的对象。我可以控制我传递的类名,我可以获得一个指向新处理程序对象的指针(通过getHandler()调用),我想观察使用Mockito调用它。

我目前的解决方案是:

  1. 创建一个实现接口的新测试类TestHandler
  2. 让该测试类包含一个Mockito模拟对象,该对象也实现了该接口。
  3. 手动将所有接口方法传递给模拟对象。
  4. 通过getMock()方法访问模拟对象。
  5. 通过对objectUnderTest.getHandler().getMock()进行verify()调用来观察对象。
  6. 这有效,但感觉有点不雅,特别是必须手动编写所有pass-thru方法。

    有更好的解决方案吗?

2 个答案:

答案 0 :(得分:1)

Fundamentally, you're running into the same problems as trying to test a newly-created instance using new; the Class.newInstance (probably properly Class.forName(foo).newInstance()) doesn't hurt you, but doesn't help you either.

As a side note, your TestHandler sounds like a general purpose delegate implementation, which sounds pretty useful anyway (particularly if you ever need to write a Handler wrapper). If it is, you might want to promote it to be adjacent to your Handler in your production code tree.

Though I recognize that you mention legacy code, this becomes very easy if you are allowed to refactor to include a testing seam. (Ignoring reflective exceptions here for ease of explanation.)

public ReturnType yourMethodUnderTest(String className) {
  return yourMethodUnderTest(Class.newInstance(className));
}

/** Package private for testing. */
public ReturnType yourMethodUnderTest(Handler handler) {
  return yourMethodUnderTest(Class.newInstance(className));
}

You could also extract the object creation and replace it in your test:

/** Instance field, package-private to replace in tests. */
Function<String, Handler> instanceCreator =
    ( x -> (Handler) Class.forName(x).newInstance());

public ReturnType yourMethodUnderTest(String className) {
  Handler handler = instanceCreator.apply(className);
  // ...
}

You could even just extract it to a method and replace it in your test:

public ReturnType yourMethodUnderTest(String className) {
  Handler handler = createHandler(className);
  // ...
}

/** Package private for testing. */
Handler createHandler(String className) {
  return Class.forName(className).newInstance();
}

@Test public void yourTest() {
  // Manually replace createHandler. You could also use a Mockito spy here.
  ObjectUnderTest objectUnderTest = new ObjectUnderTest() {
    @Override Handler createHandler(String className) {
      return mock(Handler.class);
    }
  }
  // ...
}

Side note: Even though Mockito creates a named dynamic type, you almost certainly will not be able to hack it in and allow your code to create it by name. This is because the call to mock registers the instance within Mockito's internal state.

// BAD: Unlikely to work
@Test public void yourTest() {
  objectUnderTest.methodUnderTest(
      mock(Handler.class).getClass().getName());
  // ...
}

答案 1 :(得分:0)

创建一个公共方法,您将在其中放置获取类的newInstance的逻辑

ClassA objectClassA=createNewInstance(className);

同样,并且

public ClassA createInstance(String className){
 return (ClassA) (Class.forName(className)).newInstance();
}

现在假设我们正在ClassB内创建classA的实例 然后在B的TestClass中,我们可以简单地模拟此createInstance方法

doReturn(mockClassA).when(mockClassB).createInstance(className);