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

我当前的解决方案是:


创建一个新的测试类TestHandler来实现该接口。
让该测试类包含一个Mockito模拟对象,该对象也实现该接口。
手动将所有接口方法传递给模拟对象。
通过getMock()方法可访问模拟对象。
通过对objectUnderTest.getHandler().getMock()进行verify()调用来观察对象。


这可行,但感觉有点不雅致,尤其是必须手动编写所有直通方法。

有更好的解决方案吗?

最佳答案

从根本上讲,您遇到了与尝试使用new测试新创建的实例相同的问题。 Class.newInstance(可能是正确的Class.forName(foo).newInstance())不会伤害您,但也不会帮助您。

附带说明一下,您的TestHandler听起来像一个通用的委托实现,无论如何听起来都非常有用(特别是如果您需要编写Handler包装器的话)。如果是这样,您可能希望将其提升为与生产代码树中的Handler相邻。

尽管我认识到您提到了遗留代码,但是如果允许您重构以包含测试接缝,这将变得非常容易。 (为了便于说明,此处忽略反射异常。)

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

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


您还可以提取对象创建并在测试中将其替换:

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


您甚至可以将其提取为方法并在测试中将其替换:

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




旁注:即使Mockito创建了命名的动态类型,您几乎肯定也将无法破解它,并允许您的代码按名称创建它。这是因为对mock registers the instance within Mockito's internal state的调用。

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

关于java - 如何模拟通过Class.newInstance(className)创建的对象?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35366965/

10-12 00:23
查看更多